图书列表页面

接下来,我们将实现我们的图书列表页面。此页面需要显示数据库中所有图书的列表,以及它们的作者,每个图书标题都是指向其关联的图书详情页面的超链接。

控制器

图书列表控制器函数需要获取数据库中所有Book对象列表,对其进行排序,然后将这些对象传递给模板进行渲染。

打开/controllers/bookController.js。找到导出的book_list()控制器方法,并将其替换为以下代码。

js
// Display list of all books.
exports.book_list = asyncHandler(async (req, res, next) => {
  const allBooks = await Book.find({}, "title author")
    .sort({ title: 1 })
    .populate("author")
    .exec();

  res.render("book_list", { title: "Book List", book_list: allBooks });
});

路由处理程序在Book模型上调用find()函数,选择仅返回titleauthor,因为我们不需要其他字段(它还会返回_id和虚拟字段),并使用sort()方法按标题字母顺序对结果进行排序。我们还在Book上调用populate(),指定author字段——这将用完整的作者详细信息替换存储的图书作者 ID。然后,exec()在末尾链接,以执行查询并返回一个 Promise。

路由处理程序使用await等待 Promise,暂停执行直到 Promise 完成。如果 Promise 已完成,则查询结果将保存到allBooks变量中,处理程序继续执行。

路由处理程序的最后一部分调用render(),指定book_list(.pug)模板并将titlebook_list的值传递到模板中。

视图

创建/views/book_list.pug并将下面的文本复制到其中。

pug
extends layout

block content
  h1= title
  if book_list.length
    ul
      each book in book_list
        li
          a(href=book.url) #{book.title}
          |  (#{book.author.name})

  else
    p There are no books.

该视图扩展了layout.pug基本模板并覆盖了名为'content'的block。它显示我们从控制器(通过render()方法)传入的title,并使用each-in语法遍历book_list变量。为每本书创建一个列表项,显示图书标题作为指向图书详情页面的链接,后跟作者姓名。如果book_list中没有图书,则执行else子句,并显示文本“没有图书”。

注意:我们使用book.url为每本书提供指向详细信息记录的链接(我们已经实现了此路由,但尚未实现页面)。这是Book模型的一个虚拟属性,它使用模型实例的_id字段生成唯一的 URL 路径。

这里值得注意的是,每本书被定义为两行,使用管道符作为第二行。需要这种方法是因为如果作者姓名在上一行,那么它将成为超链接的一部分。

它是什么样子的?

运行应用程序(有关相关命令,请参阅测试路由),并在浏览器中打开https://127.0.0.1:3000/。然后选择“所有图书”链接。如果一切设置正确,您的网站应该如下面的屏幕截图所示。

Book List Page - Express Local Library site

后续步骤