主頁

我們要建立的第一個頁面是網站的主頁,可以從站點 (/) 或目錄 (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 = 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");

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

用以下程式碼片段替換以上所有程式碼。這首先匯入(require())所有模型。我們需要這樣做,因為我們將使用它們來獲取文件計數。

js
const Book = require("../models/book");
const Author = require("../models/author");
const Genre = require("../models/genre");
const BookInstance = require("../models/bookinstance");

exports.index = 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。其他模板語言可能要求您傳遞用於所有使用的物件的值。

請注意,程式碼非常簡單,因為我們可以假設資料庫查詢會成功。如果任何資料庫操作失敗,丟擲的異常將導致 Promise 被拒絕,Express 會將錯誤傳遞給鏈中的 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”檔案中存在的不同控制器中指定的。

後續步驟