类型详情页
类型详情页需要使用其自动生成的 _id 字段值作为标识符来显示特定类型实例的信息。所需类型记录的 ID 编码在 URL 的末尾,并根据路由定义(/genre/:id)自动提取。然后在控制器中通过请求参数 req.params.id 访问它。
页面应显示类型名称以及该类型下所有图书的列表,并包含指向每本图书详情页的链接。
控制器
打开 /controllers/genreController.js 并在文件顶部引入 Book 模块(文件应该已经 require() 了 Genre 模块)。
const Book = require("../models/book");
找到导出的 genre_detail() 控制器方法,并用以下代码替换它。
// Display detail page for a specific Genre.
exports.genre_detail = 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_books: booksInGenre,
});
};
我们首先使用 Genre.findById() 获取特定 ID 的类型信息,并使用 Book.find() 获取所有具有相同关联类型 ID 的图书记录。由于两个请求不互相依赖,我们使用 Promise.all() 并行执行数据库查询(在 主页 中演示了并行查询的相同方法)。
我们 await 返回的 promise,一旦它得到解决,我们就检查结果。如果数据库中不存在该类型(即可能已被删除),那么 findById() 将成功返回但没有结果。在这种情况下,我们希望显示一个“未找到”页面,因此我们创建一个 Error 对象并将其传递给链中的 next 中间件函数。
注意: 传递给 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://: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 教程第 5 部分:显示图书馆数据。
- 继续第 5 部分的下一篇文章:图书详情页。