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

MongoDB PUT请求无法更新read字段修改EJS元素类名问题

问题背景
  • 开发项目为The Odin Project的Library Project,采用MongoDB作为数据存储方案
  • 数据库books集合中read字段存储值为字符串类型的"true"/"false",非布尔值
  • 实现目标:为.change类按钮绑定点击事件,触发PUT请求切换对应书籍的read字段值;更新完成后同步修改EJS渲染元素的类名,根据read值动态改变书籍条目显示颜色
  • 已尝试方案:调整常量定义、修改请求体read字段传值,均未生效,初步判断问题出在app.put接口逻辑
现有代码

前端JS代码

const readButton = document.querySelectorAll(".change");
Array.from(readButton).forEach((element) => {
    element.addEventListener("click", changeRead);
});
async function changeRead() {
    try {
        const response = await fetch("changeRead", {
            method: "put",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                read: "false",
            }),
        });
        const data = await response.json();
        console.log(data);
        // location.reload();
    } catch (error) {
        console.log(error);
    }
}

Node.js接口代码

注:曾尝试将查询条件的title: request.body.title修改为read:request.body.read,问题依旧存在

app.put("/changeRead", (request, response) => {
    db.collection("books")
        .updateOne(
            { title: request.body.title },
            {
                $set: {
                    read: "false",
                },
            }
        )
        .then((result) => {
            console.log("read changed");
            response.json("book status changed");
        })
        .catch((error) => console.log(error));
});

index.ejs模板代码

<% for(let i=0; i<books.length; i++) {%>
<div class="col-sm-6 col-md-4 col-lg-3 text-center">
    <div class="card book">
        <div class="row">
            <div class="col-1 binding p-0 <%= books[i].read %>" id="<%= books[i].read %>"></div>
            <div class="col-10 p-0">
                <div class="card-body p-4">
                    <h5 class="cart-title p-2">
                        <%= books[i].title %>
                    </h5>
                    <p class="card-text"><%= books[i].author %>
                    </p>
                    <p class="card-text">Pages: <%= books[i].pages %>
                    </p>
                    <p class="card-text">Published: <%= books[i].published %>
                    </p>
                    <button type="button" class="btn text-center change m-1">Change Read
                    </button>
                    <button type='button' class="btn text-center delete my-1">Delete Book
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>
<%}%>

MongoDB文档示例

{
    "_id" : "redacted",
    "title" : "The Return of the Kings",
    "author" : "J.R.R. Tolkien",
    "pages" : "416",
    "published" : "",
    "read" : "true"
}
问题根因
  • 前端请求未携带目标书籍的唯一标识:当前请求体仅硬编码传入read: "false",没有传递对应书籍的ID/标题,后端查询条件request.body.title实际为undefined,无法匹配到待更新的文档;且固定传false无法实现true/false双向切换。
  • 后端更新逻辑硬编码目标值:接口内固定将read字段设为"false",未根据书籍原有状态做切换判断;同时使用title作为查询条件容易因同名书籍匹配错误,优先使用MongoDB生成的_id作为唯一查询条件。
  • 前端无DOM更新逻辑:当前注释掉了页面刷新代码,接口请求成功后页面不会同步更新样式,无法看到类名变化带来的颜色修改效果。
修复方案

1. 修改EJS模板,给按钮绑定书籍标识

在.change按钮上添加自定义属性,存储当前书籍的_id和已读状态,方便前端点击时获取对应数据:

<button type="button" class="btn text-center change m-1" data-book-id="<%= books[i]._id.toString() %>" data-current-read="<%= books[i].read %>">Change Read
</button>

_id调用toString()是为了把MongoDB的ObjectId类型转成字符串,避免前端序列化报错。

2. 修改前端JS逻辑

点击按钮时获取对应书籍的ID和当前状态,计算切换后的目标值,传给后端;请求成功后直接更新DOM类名,无需整页刷新:

const readButton = document.querySelectorAll(".change");
Array.from(readButton).forEach((element) => {
    element.addEventListener("click", changeRead);
});

async function changeRead(e) {
    const currentBtn = e.currentTarget;
    const bookId = currentBtn.dataset.bookId;
    const currentReadStatus = currentBtn.dataset.currentRead;
    // 计算切换后的状态:当前是true就切为false,反之切为true
    const newReadStatus = currentReadStatus === "true" ? "false" : "true";
    
    try {
        const response = await fetch("/changeRead", {
            method: "PUT",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                bookId: bookId,
                newRead: newReadStatus
            }),
        });
        const result = await response.json();
        if (result.success) {
            // 更新按钮上存储的状态值,方便下次点击计算
            currentBtn.dataset.currentRead = newReadStatus;
            // 找到当前书籍卡片侧边的binding元素,替换类名和id
            const bindingBar = currentBtn.closest(".book").querySelector(".binding");
            bindingBar.classList.remove(currentReadStatus);
            bindingBar.classList.add(newReadStatus);
            bindingBar.id = newReadStatus;
        }
    } catch (error) {
        console.log(error);
    }
}

3. 修改后端PUT接口

引入ObjectId做类型转换,通过书籍_id查询更新对应文档,返回操作结果:

// 文件顶部引入ObjectId,适配MongoDB官方驱动
const { ObjectId } = require("mongodb");

app.put("/changeRead", async (request, response) => {
    try {
        const { bookId, newRead } = request.body;
        const updateResult = await db.collection("books").updateOne(
            { _id: new ObjectId(bookId) },
            { $set: { read: newRead } }
        );
        if (updateResult.modifiedCount === 1) {
            response.json({ success: true, msg: "书籍阅读状态更新成功" });
        } else {
            response.json({ success: false, msg: "未找到对应书籍或状态无变化" });
        }
    } catch (error) {
        console.log(error);
        response.status(500).json({ success: false, msg: "服务端错误" });
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 12:57:14