首頁

我們將建立的第一個頁面是網站首頁,可以透過站點 (/) 或目錄 (catalog/) 根目錄訪問。它將顯示一些描述站點的靜態文字,以及資料庫中不同記錄型別的動態計算的“計數”。

我們已經為首頁建立了一個路由。為了完成該頁面,我們需要更新我們的控制器函式以從資料庫中獲取記錄的“計數”,並建立一個檢視(模板)來渲染頁面。

注意:我們將使用 Mongoose 獲取資料庫資訊。在繼續之前,您可能希望重新閱讀 Mongoose 入門 部分中關於 搜尋記錄 的內容。

路由

我們在 之前的教程 中建立了索引頁路由。提醒一下,所有路由函式都在 /routes/catalog.js 中定義。

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 中定義了一個“佔位符”實現。

js
exports.index = asyncHandler(async (req, res, next) => {
  res.send("NOT IMPLEMENTED: Site Home Page");
});

我們將擴充套件此控制器函式,以從我們的模型中獲取資訊,然後使用模板(檢視)進行渲染。

控制器

索引控制器函式需要獲取有關資料庫中 BookBookInstance(全部)、BookInstance(可用)、AuthorGenre 記錄數量的資訊,將這些資料渲染到模板中以建立 HTML 頁面,然後將其返回到 HTTP 響應中。

開啟 /controllers/bookController.js。在檔案頂部附近,您應該會看到匯出的 index() 函式。

js
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”,它提供了一個包裝器來 捕獲路由處理程式函式中丟擲的異常

js
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 並將其內容替換為以下文字。

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://:3000/。如果一切設定正確,您的站點應該看起來像以下螢幕截圖。

Home page - Express Local Library site

注意:您還不能使用側邊欄連結,因為這些頁面的 URL、檢視和模板尚未定義。如果您嘗試,您將收到錯誤,例如“未實現:書籍列表”,具體取決於您單擊的連結。這些字串文字(將被替換為適當的資料)在“controllers”檔案中存在的不同控制器中指定。

後續步驟