iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 14
1
Modern Web

站在Web前端人員角度,學習 Spring Boot 後端開發系列 第 14

Day 14 - Spring Boot ToDoList 使用Template Engine 實作(2)

  • 分享至 

  • xImage
  •  

是你的思緒把我想的太美好

上一篇已經實作的TodoLidt [新增代辦事項]與[顯示代辦事項列表],今天要實作[更新代辦事項狀態]與[刪除代辦事項]

TodoService.java Service

在service層增加3個方法,分別是updateTodofindByIddeleteTodo

updateTodo 透過todoDao 的save()方法操作資料庫,deleteTodo 則透過todoDao 的deleteById(id)方法刪除資料庫物件。findById 可以先確定資料庫是否有這筆值,再進行更新或刪除資料。

public Todo updateTodo(Integer id,Todo todo) {
        try {
            Todo resTodo = findById(id);
            Integer status = todo.getStatus();
            resTodo.setStatus(status);
            return todoDao.save(resTodo);
        }catch (Exception exception) {
            return null;
        }

    }

 public Todo findById(Integer id) {
        Todo todo = todoDao.findById(id).get();
        return todo;
 }

 public Boolean deleteTodo(Integer id) {
     try {
            Todo resTodo = findById(id);
            todoDao.deleteById(id);
            return true;
        } catch (Exception exception) {
            return false;
        }
  }

TodoController.java Controller

新增@PutMapping("/todos/{id}") 更新資料的方法與@DeleteMapping("/todos/{id}") 刪除資料的方法,而參數中的@PathVariable 對應到url的 {id} , @RequestBody 前端請求過來的body。

@ResponseBody
@PutMapping("/todos/{id}")
    public void upadteTodo(@PathVariable Integer id, @RequestBody Todo todo) {
        todoService.updateTodo(id ,todo);
    }
    
@ResponseBody
@DeleteMapping("/todos/{id}")
    public void deleteTodo(@PathVariable Integer id) {
        todoService.deleteTodo(id);
    }

todolist.html

更新todo項目的狀態時,會呼叫 js 的updateTodo() 利用fetch將資料HTT PUT至Controller做資料庫的更新,待成功後, 更新<li>節點的css改成"checked,就可以看到代辦事項顯示綠色。

刪除todo項目時,會呼叫 js 的deleteTodo() 利用fetch將資料HTT DELETE至Controller做資料庫的移除,並將畫面代辦事項的節點移除。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" th:href="@{/style.css}">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
    <title>Todo List</title>
</head>
<body>
<div class="container">
    <h2>To Do List</h2>
    <form class="header" th:action="@{/todos}" method="post" th:object="${todoObject}">
      <input type="text" id="input" placeholder="New Item..." th:field="*{task}">
      <button type="submit" class="addBtn">Add</button>
    </form>
    <ul th:each="todo: ${todolist}">
        <li th:class="${todo.status} == 2 ? 'checked': '' "
            th:onclick="'javascript:updateTodo(this,' + ${todo.id} + ','+${todo.status} +')'">
            <span th:text="${todo.task}"></span>
            <span class="close" th:onclick="'javascript:deleteTodo(event,this,' + ${todo.id} + ')'">x</span>
        </li>
    </ul>
</div>
<script>
    const updateTodo = (element, id, status) => {
        console.log("status", status);
        const notDone = 1;
        const done = 2;
        const data = {
            status: status === done ? notDone : done
        }
        fetch('todos/'+ id, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data),
            }).then(function(response) {
                if (status === done) {
                    element.classList.remove("checked");
                } else  {
                    element.classList.add("checked");
                }
        })
        location.reload();
    }

    const deleteTodo = (event, element, id) => {
        event.stopPropagation();
        fetch('todos/'+ id, {
            method: 'DELETE',
            headers: {
                'Content-Type': 'application/json'
            },
        }).then(function(response) {
             element.parentNode.parentNode.parentNode.removeChild(element.parentNode.parentNode);
       })
    }
</script>
</body>
</html>

https://ithelp.ithome.com.tw/upload/images/20200923/20118857rqp3udRukj.png


[更新]

在實作的過程中有遇到一些問題,以下條列遇到的問題以及該如何解決。
問題一
我透過POST提交了表單。提交了表單後,重新刷新頁面時,瀏覽器會詢問「確認重新提交表單」按確定之後就會重新POST /todos一模一樣的內容,這樣畫面列表會多一列,Web表單通過HTTP POST請求提交給server而刷新頁面時會導致內容重複提交,可以採取Post / Redirect / Get 設計模式,為的是轉換POST請求,避免二次提交。
https://ithelp.ithome.com.tw/upload/images/20200924/20118857X5KXxCeIWX.png

要在TodoController.java 檔案的createTodo() 做一個修正
原本:

    @PostMapping("/todos")
    public String createTodo(@ModelAttribute Todo todo, Model model) {
        Iterable<Todo> allTodoList = todoService.createTodo(todo);
        Todo emptyTodo = new Todo();
        model.addAttribute("todolist", allTodoList);
        model.addAttribute("todoObject", emptyTodo);
        return "todolist";
    }

改成:

    @PostMapping("/todos")
    public String createTodo(@ModelAttribute Todo todo, Model model) {
        Iterable<Todo> allTodoList = todoService.createTodo(todo);
        Todo emptyTodo = new Todo();
        model.addAttribute("todolist", allTodoList);
        model.addAttribute("todoObject", emptyTodo);
        return "redirect:/todos";
    }

要將POST的請求完後,去重新Redirect到/todos 形成GET /todos,讓畫面重新取值。


今天的實作有點卡住了,加上趕趕的情形下,不太能靜下心,有幾個問題待釐清,會再更新此篇文章,讓學習更加完整。
雖然遇到問題在所難免,有點灰心的是,基礎觀念在一開始就沒有弄清楚,問題出現時,無從判斷起,今天晚晚回家時,室友們的溫暖歌聲治癒了我現在這種無力感/images/emoticon/emoticon28.gif


上一篇
Day 13 - Spring Boot ToDoList 使用Template Engine 實作(1)
下一篇
Day 15 - Spring Boot 向前後端分離say hi ~
系列文
站在Web前端人員角度,學習 Spring Boot 後端開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
拼其兒
iT邦新手 5 級 ‧ 2020-09-24 09:21:57

說到"基礎觀念在一開始就沒有弄清楚"這跟我學習剪片狀況很像,但我相信狀況會越來越好的!
學習永無止盡,願意挑戰就是一件很值得欽佩的事了~

cailiwu iT邦新手 4 級 ‧ 2020-09-24 15:42:47 檢舉

謝謝你的鼓勵:))
透過不斷的學習探索,會漸漸找到節奏的
/images/emoticon/emoticon41.gif

0
darta0809
iT邦新手 5 級 ‧ 2021-01-05 17:59:17

妳好,跟著妳實作的過程中,不知道我理解有沒有錯

似乎沒有看到setStatus為2的地方
目前都是null的狀態

在controller的updateTodo方法中
我加入了todo.setStatus(2)
DB這邊才會把status狀態update為2

cailiwu iT邦新手 4 級 ‧ 2021-01-05 22:29:41 檢舉

你好:
很感謝您提出疑問,我嘗試理解一下您提出的問題,
你敘述到:沒有看到setStatus為2的地方,需要在controller裡將todo.setStatus(2)才能將狀態改為2。

這是一支 update todo 的api
api的格式如下:

 - Request
 PUT
 url: /todos/{id}
 body: {status:2}

所以前端(template)js用fetch 呼叫api時,會在body裡放{status: 2},傳至後端,並在TodoService.javaupdateTodo()方法處理,將資料庫這筆資料的status設為前端傳進來的值,如下:

  Integer status = todo.getStatus();
  resTodo.setStatus(status);

前端template裡js的部份代碼

const data = {
    status: status === done ? notDone : done
}
fetch('todos/'+ id, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data),
            }).then(function(response) {
                if (status === done) {
                    element.classList.remove("checked");
                } else  {
                    element.classList.add("checked");
                }
        })

希望這樣解釋有解決你的疑問。

darta0809 iT邦新手 5 級 ‧ 2021-01-06 11:34:57 檢舉

謝謝妳~解決了!

0
shawnchill717
iT邦新手 4 級 ‧ 2022-02-20 23:29:02

妳寫得很好啊
經過一年多後仍然非常受用XD
期待有新的教學

我要留言

立即登入留言