首页
我们将创建的第一个页面是网站首页,可以通过站点 (/
) 或目录 (catalog/
) 根目录访问。它将显示一些描述站点的静态文本,以及数据库中不同记录类型的动态计算的“计数”。
我们已经为首页创建了一个路由。为了完成该页面,我们需要更新我们的控制器函数以从数据库中获取记录的“计数”,并创建一个视图(模板)来渲染页面。
注意:我们将使用 Mongoose 获取数据库信息。在继续之前,您可能希望重新阅读 Mongoose 入门 部分中关于 搜索记录 的内容。
路由
我们在 之前的教程 中创建了索引页路由。提醒一下,所有路由函数都在 /routes/catalog.js 中定义。
// GET catalog home page.
router.get("/", book_controller.index); //This actually maps to /catalog/ because we import the route with a /catalog prefix
作为参数传递的书籍控制器索引函数 (book_controller.index
) 在 /controllers/bookController.js 中定义了一个“占位符”实现。
exports.index = asyncHandler(async (req, res, next) => {
res.send("NOT IMPLEMENTED: Site Home Page");
});
我们将扩展此控制器函数,以从我们的模型中获取信息,然后使用模板(视图)进行渲染。
控制器
索引控制器函数需要获取有关数据库中 Book
、BookInstance
(全部)、BookInstance
(可用)、Author
和 Genre
记录数量的信息,将这些数据渲染到模板中以创建 HTML 页面,然后将其返回到 HTTP 响应中。
打开 /controllers/bookController.js。在文件顶部附近,您应该会看到导出的 index()
函数。
const Book = require("../models/book");
const asyncHandler = require("express-async-handler");
exports.index = asyncHandler(async (req, res, next) => {
res.send("NOT IMPLEMENTED: Site Home Page");
});
将上面所有代码替换为以下代码片段。首先,它导入(require()
)所有模型。我们需要这样做,因为我们将使用它们来获取文档的计数。该代码还要求“express-async-handler”,它提供了一个包装器来 捕获路由处理程序函数中抛出的异常。
const Book = require("../models/book");
const Author = require("../models/author");
const Genre = require("../models/genre");
const BookInstance = require("../models/bookinstance");
const asyncHandler = require("express-async-handler");
exports.index = asyncHandler(async (req, res, next) => {
// Get details of books, book instances, authors and genre counts (in parallel)
const [
numBooks,
numBookInstances,
numAvailableBookInstances,
numAuthors,
numGenres,
] = await Promise.all([
Book.countDocuments({}).exec(),
BookInstance.countDocuments({}).exec(),
BookInstance.countDocuments({ status: "Available" }).exec(),
Author.countDocuments({}).exec(),
Genre.countDocuments({}).exec(),
]);
res.render("index", {
title: "Local Library Home",
book_count: numBooks,
book_instance_count: numBookInstances,
book_instance_available_count: numAvailableBookInstances,
author_count: numAuthors,
genre_count: numGenres,
});
});
我们使用 countDocuments()
方法获取每个模型的实例数量。此方法在模型上调用,并带有一组可选的匹配条件,并返回一个 Query
对象。可以通过调用 exec()
来执行查询,该方法返回一个 Promise
,该 Promise
或者以结果完成,或者如果存在数据库错误则被拒绝。
因为文档计数的查询彼此独立,所以我们使用 Promise.all()
并行运行它们。该方法返回一个新的 promise,我们 await
其完成(执行在 此函数 中的 await
处暂停)。当所有查询完成后,all()
返回的 promise 将完成,继续执行路由处理程序函数,并使用数据库查询的结果填充数组。
然后我们调用 res.render()
,指定一个名为“index”的视图(模板)和对象,将数据库查询的结果映射到视图模板。数据作为键值对提供,可以使用键在模板中访问。
注意:如果您在 Pug 模板中使用了未传入的键/变量,则它将呈现为空字符串,并在表达式中被评估为 false
。其他模板语言可能要求您为使用到的所有对象传入值。
请注意,代码非常简单,因为我们可以假设数据库查询成功。如果任何数据库操作失败,则抛出的异常将被 asyncHandler()
捕获并传递给链中的 next
中间件处理程序。
视图
打开 /views/index.pug 并将其内容替换为以下文本。
extends layout
block content
h1= title
p Welcome to #[em LocalLibrary], a very basic Express website developed as a tutorial example on the Mozilla Developer Network.
h2 Dynamic content
p The library has the following record counts:
ul
li #[strong Books:] !{book_count}
li #[strong Copies:] !{book_instance_count}
li #[strong Copies available:] !{book_instance_available_count}
li #[strong Authors:] !{author_count}
li #[strong Genres:] !{genre_count}
视图很简单。我们扩展了 layout.pug 基本模板,覆盖了名为“content”的 block
。第一个 h1
标题将是传递到 render()
函数的 title
变量的转义文本——请注意使用“h1=
”,以便以下文本被视为 JavaScript 表达式。然后,我们包含一段介绍 LocalLibrary 的文字。
在“动态内容”标题下,我们列出了每个模型的副本数量。请注意,数据的模板值是在路由处理程序函数中调用 render()
时指定的键。
注意:我们没有转义计数值(即,我们使用了 !{}
语法),因为计数值是计算出来的。如果信息是由最终用户提供的,那么我们将转义变量以进行显示。
它是什么样子的?
此时,我们应该已经创建了显示索引页面所需的一切。运行应用程序并在浏览器中打开 https://127.0.0.1:3000/
。如果一切设置正确,您的站点应该看起来像以下屏幕截图。
注意:您还不能使用侧边栏链接,因为这些页面的 URL、视图和模板尚未定义。如果您尝试,您将收到错误,例如“未实现:书籍列表”,具体取决于您单击的链接。这些字符串文字(将被替换为适当的数据)在“controllers”文件中存在的不同控制器中指定。
后续步骤
- 返回 Express 教程第 5 部分:显示库数据。
- 继续第 5 部分的下一篇文章:书籍列表页面。