型別詳情頁面

詳情 頁面需要顯示特定型別例項的資訊,使用其自動生成的 _id 欄位值作為識別符號。所需型別記錄的 ID 編碼在 URL 的末尾,並根據路由定義 (/genre/:id) 自動提取。然後,它可以透過請求引數在控制器中訪問:req.params.id

頁面應該顯示型別名稱以及型別中所有書籍的列表,幷包含指向每本書詳情頁面的連結。

控制器

開啟 /controllers/genreController.js 並將 Book 模組匯入到檔案頂部(檔案應該已經 require()Genre 模組和 "express-async-handler")。

js
const Book = require("../models/book");

找到匯出的 genre_detail() 控制器方法,並將其替換為以下程式碼。

js
// Display detail page for a specific Genre.
exports.genre_detail = asyncHandler(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,
    genre_books: booksInGenre,
  });
});

我們首先使用 Genre.findById() 獲取特定 ID 的型別資訊,並使用 Book.find() 獲取所有具有相同關聯型別 ID 的書籍記錄。由於這兩個請求彼此之間沒有依賴關係,因此我們使用 Promise.all() 並行執行資料庫查詢(這種並行執行查詢的方法在 首頁 中演示過)。

我們對返回的 Promise 進行 await 操作,並在其穩定後檢查結果。如果型別不存在於資料庫中(即可能已被刪除),那麼 findById() 將成功返回,但沒有結果。在這種情況下,我們希望顯示一個“未找到”頁面,因此我們建立一個 Error 物件,並將其傳遞給鏈中的 next 中介軟體函式。

注意: 傳遞給 next 中介軟體函式的錯誤會傳播到我們的錯誤處理程式碼(這是我們在 生成應用程式框架 時設定的 - 有關更多資訊,請參閱 處理錯誤)。

如果找到 genre,那麼我們將呼叫 render() 來顯示檢視。檢視模板是 genre_detail (.pug)。標題、genrebooksInGenre 的值使用相應的鍵 (titlegenregenre_books) 傳遞到模板中。

檢視

建立 /views/genre_detail.pug 並使用以下文字填充它

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/。選擇 所有型別 連結,然後選擇其中一個型別(例如“奇幻”)。如果一切都設定正確,您的頁面應該類似於以下螢幕截圖。

Genre Detail Page - Express Local Library site

注意: 如果 req.params.id(或任何其他 ID)無法轉換為 mongoose.Types.ObjectId(),您可能會收到類似於下面的錯誤。

bash
Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre"

最可能的原因是傳遞到 mongoose 方法的 ID 實際上不是 ID。 Mongoose.prototype.isValidObjectId() 可用於檢查特定 ID 是否有效。

後續步驟