类型详情页面
详情 页面需要显示特定类型实例的信息,使用其自动生成的 _id
字段值作为标识符。所需类型记录的 ID 编码在 URL 的末尾,并根据路由定义 (/genre/:id) 自动提取。然后,它可以通过请求参数在控制器中访问:req.params.id
。
页面应该显示类型名称以及类型中所有书籍的列表,并包含指向每本书详情页面的链接。
控制器
打开 /controllers/genreController.js 并将 Book
模块导入到文件顶部(文件应该已经 require()
了 Genre
模块和 "express-async-handler")。
const Book = require("../models/book");
找到导出的 genre_detail()
控制器方法,并将其替换为以下代码。
// Display detail page for a specific Genre.
exports.genre_detail = asyncHandler(async (req, res, next) => {
// Get details of genre and all associated books (in parallel)
const [genre, booksInGenre] = await Promise.all([
Genre.findById(req.params.id).exec(),
Book.find({ genre: req.params.id }, "title summary").exec(),
]);
if (genre === null) {
// No results.
const err = new Error("Genre not found");
err.status = 404;
return next(err);
}
res.render("genre_detail", {
title: "Genre Detail",
genre: genre,
genre_books: booksInGenre,
});
});
我们首先使用 Genre.findById()
获取特定 ID 的类型信息,并使用 Book.find()
获取所有具有相同关联类型 ID 的书籍记录。由于这两个请求彼此之间没有依赖关系,因此我们使用 Promise.all()
并行运行数据库查询(这种并行运行查询的方法在 首页 中演示过)。
我们对返回的 Promise 进行 await
操作,并在其稳定后检查结果。如果类型不存在于数据库中(即可能已被删除),那么 findById()
将成功返回,但没有结果。在这种情况下,我们希望显示一个“未找到”页面,因此我们创建一个 Error
对象,并将其传递给链中的 next
中间件函数。
如果找到 genre
,那么我们将调用 render()
来显示视图。视图模板是 genre_detail (.pug)。标题、genre
和 booksInGenre
的值使用相应的键 (title
、genre
和 genre_books
) 传递到模板中。
视图
创建 /views/genre_detail.pug 并使用以下文本填充它
extends layout
block content
h1 Genre: #{genre.name}
div(style='margin-left:20px;margin-top:20px')
h2(style='font-size: 1.5rem;') Books
if genre_books.length
dl
each book in genre_books
dt
a(href=book.url) #{book.title}
dd #{book.summary}
else
p This genre has no books.
该视图与我们所有其他模板非常相似。主要区别在于我们没有使用传递进来的 title
作为第一个标题(尽管它用于底层的 layout.pug 模板来设置页面标题)。
它是什么样的?
运行应用程序并在浏览器中打开 https://127.0.0.1:3000/
。选择 所有类型 链接,然后选择其中一个类型(例如“奇幻”)。如果一切都设置正确,您的页面应该类似于以下屏幕截图。
注意: 如果 req.params.id
(或任何其他 ID)无法转换为 mongoose.Types.ObjectId()
,您可能会收到类似于下面的错误。
Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre"
最可能的原因是传递到 mongoose 方法的 ID 实际上不是 ID。 Mongoose.prototype.isValidObjectId()
可用于检查特定 ID 是否有效。
后续步骤
- 返回 Express 教程 第五部分:显示库数据。
- 继续第五部分的下一个子文章:书籍详情页面。