Genre-Detailseite

Die Detail-Seite eines Genres muss die Informationen für eine bestimmte Genre-Instanz anzeigen, wobei der automatisch generierte Wert des _id-Felds als Kennung verwendet wird. Die ID des erforderlichen Genre-Datensatzes ist am Ende der URL kodiert und wird basierend auf der Routendefinition (/genre/:id) automatisch extrahiert. Sie wird dann innerhalb des Controllers über die Anfrageparameter req.params.id abgerufen.

Die Seite sollte den Namen des Genres und eine Liste aller Bücher in diesem Genre mit Links zu den jeweiligen Buchdetailseiten anzeigen.

Controller

Öffnen Sie /controllers/genreController.js und fügen Sie das Book-Modul am Anfang der Datei hinzu (die Datei sollte bereits das Genre-Modul und "express-async-handler" require()).

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

Finden Sie die exportierte genre_detail()-Controllermethode und ersetzen Sie sie durch den folgenden Code.

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,
  });
});

Wir verwenden zuerst Genre.findById(), um die Genre-Informationen für eine bestimmte ID abzurufen, und Book.find(), um alle Buchdatensätze abzurufen, die dieselbe zugehörige Genre-ID haben. Da die beiden Anfragen nicht voneinander abhängig sind, verwenden wir Promise.all(), um die Datenbankabfragen parallel auszuführen. Diese Vorgehensweise zum parallelen Ausführen von Abfragen wurde bereits auf der Startseite demonstriert.

Wir await das zurückgegebene Versprechen, und sobald es abgeschlossen ist, überprüfen wir die Ergebnisse. Wenn das Genre in der Datenbank nicht existiert (z.B. könnte es gelöscht worden sein), dann wird findById() erfolgreich mit keinen Ergebnissen zurückkehren. In diesem Fall möchten wir eine "nicht gefunden"-Seite anzeigen, daher erstellen wir ein Error-Objekt und übergeben es der next-Middleware-Funktion in der Kette.

Hinweis: Fehler, die an die next-Middleware-Funktion übergeben werden, gelangen in unseren Fehlerbehandlungscode (dies wurde eingerichtet, als wir das App-Skelett generierten — für weitere Informationen siehe Fehlerbehandlung).

Wenn das genre gefunden wird, rufen wir render() auf, um die Ansicht anzuzeigen. Das Ansichts-Template ist genre_detail (.pug). Die Werte für den Titel, genre und booksInGenre werden mit den entsprechenden Schlüsseln (title, genre und genre_books) in das Template übergeben.

Ansicht

Erstellen Sie /views/genre_detail.pug und füllen Sie sie mit dem untenstehenden Text:

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.

Die Ansicht ist sehr ähnlich zu all unseren anderen Templates. Der Hauptunterschied besteht darin, dass wir den übergebenen title nicht für die erste Überschrift verwenden (obwohl er im darunterliegenden layout.pug-Template verwendet wird, um den Seitentitel festzulegen).

Wie sieht es aus?

Führen Sie die Anwendung aus und öffnen Sie Ihren Browser unter http://localhost:3000/. Wählen Sie den Link Alle Genres, dann wählen Sie eines der Genres aus (z.B. "Fantasy"). Wenn alles korrekt eingerichtet ist, sollte Ihre Seite ungefähr wie der folgende Screenshot aussehen.

Genre-Detailseite - Express Local Library-Seite

Hinweis: Sie könnten einen Fehler ähnlich dem unten stehenden erhalten, wenn req.params.id (oder eine andere ID) nicht in eine mongoose.Types.ObjectId() umgewandelt werden kann.

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

Die wahrscheinlichste Ursache ist, dass die in die mongoose-Methoden übergebene ID tatsächlich keine ID ist. Mongoose.prototype.isValidObjectId() kann verwendet werden, um zu überprüfen, ob eine bestimmte ID gültig ist.

Nächste Schritte