如何用Mongoose、MongoDB实现待办项在集合间的迁移?
实现Mongoose中待办事项在已完成/未完成列表间的迁移功能
首先得提一句:你当前的代码里有个关键问题——你声明了todoList和compList两个空数组,但实际上在首页路由里,你把查询到的所有Todo文档赋值给了局部的todoList变量,而compList始终是空的。这说明你还没在数据层面区分“已完成”和“未完成”的事项,所以第一步得先调整你的Schema,给待办事项加个状态标识。
步骤1:更新你的Mongoose Schema
我们需要给Todo集合添加一个completed字段,用来标记事项是否完成,默认值设为false(也就是初始状态是未完成):
var todoSchema = new mongoose.Schema({ name: String, completed: { type: Boolean, default: false } // 新增状态字段 }); var Todo = mongoose.model("Todo", todoSchema);
步骤2:修改首页路由,分别查询未完成和已完成事项
原来的路由是查所有Todo,现在要分成两个查询:一个查completed: false的未完成事项,一个查completed: true的已完成事项,然后把这两个列表传给模板:
app.get("/", function(req, res){ // 查询未完成事项 Todo.find({ completed: false }, function(err, todoList){ if (err) { console.log(err); return res.sendStatus(500); } // 查询已完成事项 Todo.find({ completed: true }, function(err, compList){ if (err) { console.log(err); return res.sendStatus(500); } // 把两个列表传给EJS模板 res.render("todo.ejs", { todoList: todoList, compList: compList }); }); }); });
(也可以用Promise或者async/await来优化嵌套查询,不过先保持和你现有代码风格一致)
步骤3:编写迁移功能的路由
我们可以写一个POST路由(推荐用POST而非GET,因为这是修改数据的操作),接收待办事项的ID和目标状态,然后更新该文档的completed字段:
app.post("/toggleTodo/:id", function(req, res){ // 先找到对应的待办事项 Todo.findById(req.params.id, function(err, todoItem){ if (err) { console.log(err); return res.redirect("/"); } // 切换状态:如果当前是未完成,就改成已完成,反之亦然 todoItem.completed = !todoItem.completed; // 保存修改 todoItem.save(function(err){ if (err) { console.log(err); } // 保存后重定向回首页 res.redirect("/"); }); }); });
或者更简洁的写法,直接用findByIdAndUpdate:
app.post("/toggleTodo/:id", function(req, res){ Todo.findByIdAndUpdate( req.params.id, // 根据表单传的目标状态更新字段 { $set: { completed: req.body.targetStatus === "true" } }, { new: true }, // 返回更新后的文档(可选) function(err){ if (err) console.log(err); res.redirect("/"); } ); });
步骤4:在EJS模板中添加迁移按钮
假设你的模板里是这样渲染两个列表的,现在给每个事项加一个按钮,点击后提交表单触发迁移:
<!-- 未完成列表 --> <h3>未完成事项</h3> <ul> <% todoList.forEach(function(todo){ %> <li> <%= todo.name %> <!-- 点击后标记为已完成 --> <form action="/toggleTodo/<%= todo._id %>" method="POST" style="display: inline;"> <input type="hidden" name="targetStatus" value="true"> <button type="submit">标记完成</button> </form> <!-- 保留你原来的删除按钮 --> <a href="/delete/<%= todo._id %>">删除</a> </li> <% }) %> </ul> <!-- 已完成列表 --> <h3>已完成事项</h3> <ul> <% compList.forEach(function(todo){ %> <li> <%= todo.name %> <!-- 点击后移回未完成 --> <form action="/toggleTodo/<%= todo._id %>" method="POST" style="display: inline;"> <input type="hidden" name="targetStatus" value="false"> <button type="submit">移回未完成</button> </form> <a href="/delete/<%= todo._id %>">删除</a> </li> <% }) %> </ul>
额外优化建议
- 你原来的删除路由可以简化,直接用
findByIdAndRemove:
app.get("/delete/:id", function(req, res){ Todo.findByIdAndRemove(req.params.id, function(err){ if (err) console.log(err); res.redirect("/"); }); });
- 新增事项的代码里,
Todo.create已经会创建并保存文档,不需要先实例化newItem再调用create,可以简化成:
app.post("/newTodo", function(req, res){ Todo.create({ name: req.body.item }, function(err, newTodo){ if (err) { console.log(err); } else { console.log("Inserted item: " + newTodo.name); } res.redirect("/"); }); });
这样就能实现点击按钮在两个列表间迁移待办事项的功能啦!
内容的提问来源于stack exchange,提问作者Tadeas Vasko
相关产品推荐
相关产品推荐

