本筆記將接續 前篇,將以 Todo List 為例,用五大步驟進一步拆解「更新」、「刪除」兩大常見功能的開發歷程,然而資料庫本身提供不只一種能達成目的的操作方法,本筆記僅提供其中之一,想深入研究者可以自行查閱 官方文件。
Update 運作機制 edit from Alpha Camp's material
根據使用者流程設計,觸發機制分別為首頁(index)及細節頁面(detail) 的「編輯按鈕」,並由此導向「編輯表單頁(edit)」。
由於從首頁連至 edit 頁面的流程與導向首頁或新增表單頁類似,這邊直接從送出 edit 表單開始拆解。
<form action="/todos/{{ todo._id }}/edit" method="POST">
<input type="text" placeholder="name" name="name" value="{{ todo.name }}">
<button type="submit">Save</button>
<a href="/">back</a>
</form>
其中兩點特別要注意:
<input>
保留原始的資料 value="{{ todo.name }}"
前面提到 method 的部分之後才會優化,目前會先以 POST 搭配 path /edit
來設計:
app.post('/todos/:id/edit', (req, res) => {
const { id } = req.params // 從 URL 的 path 取得 id
const update = req.body // 藉 body-parser 拿到更新的內容
// ...
}
特別說明使用 Model.findByIdAndUpdate()
的好處是:可以傳入物件參數 update
一次更新一筆資料中的多個部分,而不用逐項修改。並且可選擇性加入其他參數。其中的 { new: true }
意思是優先存入最新的版本。細節請參考 Model.findByIdAndUpdate() 官方文件。
// Controller:路由內的邏輯都是分派程序
app.post('/todos/:id/edit', (req, res) => {
const { id } = req.params
const update = req.body
return Todo.findByIdAndUpdate(id, update, { new: true }) // Model:找到該 id 的 todo,並且以新的物件覆蓋更動的部分。
.then(()=> res.redirect(`/todos/${id}`)) // View:新增完成後導回細節頁面
.catch(error => console.log(error))
}
Delete 運作機制 edit from Alpha Camp's material
在首頁(index)及細節頁面(detail)都有刪除按鈕,觸發位置雖然不同,但將導向同一路由。由於 Delete 無法用 GET 傳遞,故採用 form。且前面提到 HTML 元件目前不支援 GET、POST 外的 method,所以也是之後來優化。
<!-- detail.hbs -->
<p>{{ todo.name }}</p>
<a href="/todos/{{ todo._id }}/edit">edit</a>
<a href="/">back</a>
<!-- 刪除僅能以 form 來送出 -->
<form action="/todos/{{ todo._id }}/delete" method="POST" style="display: inline;">
<button type="submit">delete</button>
</form>
先以 POST 搭配 path /delete
來設計:
app.post('/todos/:id/delete', (req, res) => {
const id = req.params.id // 存取 URL path 上的 id
// ...
})
使用 Model.findByIdAndDelete()
可以找到符合 id 的資料,直接刪除,細節可以參考 Model.findByIdAndDelete() 官方文件。
// Controller:路由內的邏輯都是分派程序
app.post('/todos/:id/delete', (req, res) => {
const id = req.params.id
return Todo.findByIdAndDelete(id) // Model:找到該 id 的 todo,並且從資料庫中刪除。
.then(() => res.redirect('/')) // View:導回首頁 index
.catch(error => console.log(error))
})
關於本系列更多內容及導讀,請閱讀作者於 Medium 個人專欄 【無限賽局玩家 Infinite Gamer | Publication – 】 上的文章 《用 JavaScript 打造全端產品的入門學習筆記》系列指南。