참고자료인 bootstrap template 은 todo list 를 html 화면에 직접 추가/제거하는 방식입니다.
기본 템플릿을 수정하여 데이터를 서버의 메모리, map에 저장하는 방식입니다.
서버를 새로 시작하면 이전 데이터는 지워지고 기본 데이터에서 시작합니다.
초기화면
기본 폴더 구조는 아래와 같습니다.
./main.go
package main
import (
"GO/tuckersGo/goWeb/web16-todo/myapp"
"log"
"net/http"
"github.com/urfave/negroni"
)
const portNumber = ":3000"
func main() {
mux := myapp.MakeNewHandler()
ng := negroni.Classic()
ng.UseHandler(mux)
log.Println("Started App")
err := http.ListenAndServe(portNumber, ng)
if err != nil {
panic(err)
}
}
./myapp/app.go
package myapp
import (
"net/http"
"strconv"
"time"
"github.com/gorilla/mux"
"github.com/unrolled/render"
)
var rd *render.Render
type Todo struct {
ID int `json:"id"`
Name string `json:"name"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
}
var todoMap map[int]*Todo
// root 에 접근하면 /todo.html 로 변경해주는 부분
func indexHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/todo.html", http.StatusTemporaryRedirect)
}
// 저장되어 있는 todo list 를 반환
func getTodoListHandler(w http.ResponseWriter, r *http.Request) {
list := []*Todo{}
for _, v := range todoMap {
list = append(list, v)
}
rd.JSON(w, http.StatusOK, list)
}
// 추가
func addTodoHandler(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
id := len(todoMap) + 1
todo := &Todo{id, name, false, time.Now()}
todoMap[id] = todo
rd.JSON(w, http.StatusOK, todo)
}
type Success struct {
Success bool `json:"success"`
}
// 삭제
func removeTodoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
if _, ok := todoMap[id]; ok {
delete(todoMap, id)
rd.JSON(w, http.StatusOK, Success{Success: true})
} else {
rd.JSON(w, http.StatusOK, Success{Success: false})
}
}
// 완료
func completeTodoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
complete := r.FormValue("complete") == "true"
if todo, ok := todoMap[id]; ok {
todo.Completed = complete
rd.JSON(w, http.StatusOK, Success{Success: true})
} else {
rd.JSON(w, http.StatusOK, Success{Success: false})
}
}
// 기본 todo lists
func addTestTodos() {
todoMap[1] = &Todo{1, "Buy a milk", false, time.Now()}
todoMap[2] = &Todo{2, "Exercise", true, time.Now()}
todoMap[3] = &Todo{3, "Home work", false, time.Now()}
}
func MakeNewHandler() http.Handler {
todoMap = make(map[int]*Todo)
addTestTodos()
rd = render.New()
mux := mux.NewRouter()
mux.HandleFunc("/", indexHandler)
mux.HandleFunc("/todos", getTodoListHandler).Methods("GET")
mux.HandleFunc("/todos", addTodoHandler).Methods("POST")
mux.HandleFunc("/todos/{id:[0-9]+}", removeTodoHandler).Methods("DELETE")
mux.HandleFunc("/complete-todo/{id:[0-9]+}", completeTodoHandler).Methods("GET")
return mux
}
./public/todo.js
(function($) {
'use strict';
$(function() {
var todoListItem = $('.todo-list');
var todoListInput = $('.todo-list-input');
$('.todo-list-add-btn').on("click", function(event) {
event.preventDefault();
var item = $(this).prevAll('.todo-list-input').val();
if (item) {
console.log("send '%s' to Server", item)
// post to server
$.post("/todos", {name:item}, addItem)
todoListInput.val("");
}
});
var addItem = function(item) {
if (item.completed) {
todoListItem.append("<li class='completed'" + " id='" + item.id + "'><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' checked='checked' />" + item.name + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
} else {
todoListItem.append("<li" + " id='" + item.id + "'><div class='form-check'><label class='form-check-label'><input class='checkbox' type='checkbox' />" + item.name + "<i class='input-helper'></i></label></div><i class='remove mdi mdi-close-circle-outline'></i></li>");
}
};
$.get('/todos', function(items) {
items.forEach(e => {
addItem(e)
});
});
todoListItem.on('change', '.checkbox', function() {
var id = $(this).closest("li").attr('id');
var $self = $(this);
var complete = true
if ($(this).attr('checked')) {
complete = false
}
$.get("complete-todo/"+id+"?complete="+ complete, function(data){
if (complete) {
$self.attr('checked', 'checked');
} else {
$self.removeAttr('checked');
}
$self.closest("li").toggleClass('completed');
})
});
todoListItem.on('click', '.remove', function() {
// url: todos/id, mothod:DELETE
var id = $(this).closest("li").attr('id');
var $self = $(this);
$.ajax({
url: "todos/" + id,
type: "DELETE",
success: function(data) {
if(data.success){
$self.parent().remove();
}
}
})
});
});
})(jQuery);
./public/todo.html - 샘플 템플릿에서 li 부분을 모두 주석처리함
<div class="list-wrapper">
<ul class="d-flex flex-column-reverse todo-list">
<!-- <li>
<div class="form-check"> <label class="form-check-label"> <input class="checkbox" type="checkbox"> For what reason would it be advisable. <i class="input-helper"></i></label> </div> <i class="remove mdi mdi-close-circle-outline"></i>
</li>
<li class="completed">
<div class="form-check"> <label class="form-check-label"> <input class="checkbox" type="checkbox" checked=""> For what reason would it be advisable for me to think. <i class="input-helper"></i></label> </div> <i class="remove mdi mdi-close-circle-outline"></i>
</li>
<li>
<div class="form-check"> <label class="form-check-label"> <input class="checkbox" type="checkbox"> it be advisable for me to think about business content? <i class="input-helper"></i></label> </div> <i class="remove mdi mdi-close-circle-outline"></i>
</li>
<li>
<div class="form-check"> <label class="form-check-label"> <input class="checkbox" type="checkbox"> Print Statements all <i class="input-helper"></i></label> </div> <i class="remove mdi mdi-close-circle-outline"></i>
</li>
<li class="completed">
<div class="form-check"> <label class="form-check-label"> <input class="checkbox" type="checkbox" checked=""> Call Rampbo <i class="input-helper"></i></label> </div> <i class="remove mdi mdi-close-circle-outline"></i>
</li>
<li>
<div class="form-check"> <label class="form-check-label"> <input class="checkbox" type="checkbox"> Print bills <i class="input-helper"></i></label> </div> <i class="remove mdi mdi-close-circle-outline"></i>
</li> -->
</ul>
</div>
./public/todo.css - 변경사항이 없어서 생략함
POST 방식으로 todo list 를 2개 추가하고 추가한 리스트를 삭제한 서버쪽 로그입니다.
[참고자료]
bootstrap template - https://bbbootstrap.com/snippets/awesome-todo-list-template-25095891
'GO lang > web' 카테고리의 다른 글
[GO] Todo list - interface 구현1 (0) | 2021.11.18 |
---|---|
[GO] Todo list - refactoring(구조변경) (0) | 2021.11.17 |
[GO] Web - Restful API, TDD (0) | 2021.11.04 |
[GO] Web - 테스트 코드 작성방법(TDD) (0) | 2021.11.04 |
[GO] Web - Text, json, URL 쿼리 방식 (0) | 2021.11.04 |