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

TodoList应用添加项失败、跳转异常及TypeError错误排查解决

解决Express+MongoDB TodoList应用的三个核心问题

问题1:无法向当前列表添加项目

错误原因

  • 默认列表(Today)的添加逻辑中,变量名误用:创建的Item实例是item,但保存时调用了未定义的newItem.save(),导致保存失败。
  • 自定义列表的保存操作未等待异步完成就直接跳转,可能导致数据未持久化到数据库。

修复方法

  • 将默认列表中的newItem.save()改为item.save(),并在异步保存完成后再执行跳转。
  • 自定义列表中,将跳转逻辑放在foundList.save()的回调函数内,确保数据保存成功后再刷新页面。

问题2:添加项目后无法正确跳转

错误原因

  • 自定义列表创建时,list.save()是异步操作,未等待完成就执行res.redirect,此时数据库中可能还未生成新列表,导致跳转后找不到对应页面。
  • 自定义列表添加项目时,同样未等待foundList.save()完成就跳转,可能出现数据未更新就刷新页面的情况。

修复方法

  • 列表创建时,在list.save()的then回调中执行跳转。
  • 项目添加完成后,确保在保存操作的回调内执行跳转逻辑。

问题3:偶尔抛出「TypeError: Cannot read properties of null (reading 'items')」错误

错误原因

  • 自定义列表添加项目时,List.findOne({ name: listName })可能返回null(比如列表名被篡改或已被删除),此时直接访问foundList.items会触发空指针错误。

修复方法

  • 在操作foundList之前增加存在性判断,若未找到列表则跳转回首页或提示错误。

修复后的完整代码

app.js

//jshint esversion:6

const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");

const app = express();

app.set("view engine", "ejs");

app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));

mongoose.connect("mongodb://127.0.0.1:27017/todolistDB", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

const itemsSchema = {
  name: String,
};

const Item = mongoose.model("Item", itemsSchema);

const item1 = new Item({
  name: "Welcome to your todolist!",
});

const item2 = new Item({
  name: "Hit the + button to add a new item.",
});

const item3 = new Item({
  name: "<-- Hit this to delete an item.",
});

const defaultItems = [item1, item2, item3];

const listSchema ={
  name: String,
  items: [itemsSchema]
};

const List = mongoose.model("List", listSchema);

app.get("/", function (req, res) {
  Item.find()
    .then(function (foundItems) {
      if (foundItems.length === 0)
      {
        Item.insertMany(defaultItems)
          .then(function () {
            console.log("Successfully added default items to the DB.");
            res.redirect("/");
          })
          .catch(function (err) {
            console.log(err);
          });
      }
      else
      {
        res.render("list", { listTitle: "Today", newListItems: foundItems });
      }
    })
    .catch(function (err) {
      console.log(err);
    });
});

app.get("/:customListName", function(req,res)
{
  const customListName = req.params.customListName;

  List.findOne({name: customListName}).then(function(foundList) {
    if (!foundList) 
    {
      // 创建新列表
      const list = new List({
        name: customListName,
        items: defaultItems
      });
      list.save().then(() => {
        console.log("Successfully created new list.");
        res.redirect("/" + customListName);
      }).catch(err => {
        console.log(err);
        res.redirect("/");
      });

    } else 
    {
      // 展示已存在的列表
      res.render("list", {listTitle: foundList.name, newListItems: foundList.items});
    }
  }).catch(function(err) {
    console.log(err);
    res.redirect("/");
  });
});

app.post("/", function (req, res) 
{
  const itemName = req.body.newItem;
  const listName = req.body.list;

  const item = new Item({
    name: itemName
  });

  if (listName === "Today") 
  {
    item.save().then(() => {
      console.log("Successfully added item to Today list.");
      res.redirect("/");
    }).catch(err => {
      console.log(err);
      res.redirect("/");
    });
  }
  else 
  {
    List.findOne({ name: listName })
      .then((foundList) => {
        if (!foundList) {
          console.log("List not found:", listName);
          return res.redirect("/");
        }
        foundList.items.push(item);
        foundList.save().then(() => {
          console.log("Successfully added item to custom list.");
          res.redirect("/" + listName);
        }).catch(err => {
          console.log(err);
          res.redirect("/");
        });
      }).catch((err) => 
      {
        console.log(err);
        res.redirect("/");
      });
  }
});

app.post("/delete", function(req, res)
{
  const checkedItemId = req.body.checkbox;
  const listName = req.body.list;

  if (listName === "Today") {
    Item.findByIdAndRemove(checkedItemId).then(() => {
      console.log("Successfully removed item from Today list.");
      res.redirect("/");
    }).catch(err => {
      console.log(err);
      res.redirect("/");
    });
  } else {
    List.findOneAndUpdate(
      { name: listName },
      { $pull: { items: { _id: checkedItemId } } }
    ).then(() => {
      console.log("Successfully removed item from custom list.");
      res.redirect("/" + listName);
    }).catch(err => {
      console.log(err);
      res.redirect("/");
    });
  }
})

app.get("/about", function (req, res) {
  res.render("about");
});

app.listen(3000, function () {
  console.log("Server started on port 3000");
});

list.ejs(同步修复自定义列表删除功能)

<%- include("header") -%>

<div class="box" id="heading">
  <h1><%= listTitle %></h1>
</div>

<div class="box">
  <% newListItems.forEach(function(item){ %>

  <form action="/delete" method="post">
    <div class="item">
      <input type="checkbox" name="checkbox" value="<%= item._id %>" onchange="this.form.submit()" />
      <input type="hidden" name="list" value="<%= listTitle %>">
      <p><%= item.name %></p>
    </div>
  </form>

  <% }); %>

  <form class="item" action="/" method="post">
    <input
      type="text"
      name="newItem"
      placeholder="New Item"
      autocomplete="off"
    >
    <button type="submit" name="list" value="<%= listTitle %>">+</button>
  </form>
</div>

<%- include("footer") -%>

内容的提问来源于stack exchange,提问作者Jaydip_Baraiya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:07:01