You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:42:51