express locallibrary 教程作者姓名从图书列表中丢失

问题描述 投票:0回答:1
在前端花了几年时间,并在精彩的

FreeCodeCamp 中对 Basic Node 和 Express 进行了简短介绍之后,我正在尝试建立一些后端开发知识(因为我需要它!)课程 我现在正在尝试遵循 MDN Express Locallibrary 教程。一切都很顺利,直到出现图书列表页面。

当我单击“所有书籍”链接时,应用程序应呈现所有书籍的列表以及每本书的作者姓名。我可以正常获取图书清单,但缺少作者姓名。

我本身没有收到任何错误消息,但是,当我将鼠标悬停在 allBooks

中的

async handler

 变量上(我正在使用 VS Code)时,我注意到 
author
 值似乎有些不对劲,因为
author
 键后面有一个问号,值有 
Types.ObjectID
 后跟 
null | undefined

const allBooks: Omit<Document<unknown, {}, {
    title: string;
    summary: string;
    isbn: string;
    genre: Types.ObjectId[];
    author?: Types.ObjectId | null | undefined;
}> & {
    title: string;
    summary: string;
    isbn: string;
    genre: Types.ObjectId[];
    author?: Types.ObjectId | null | undefined;
} & {
    ...;
}, never>[]

现在,我最初手动输入了大部分代码,只是为了习惯使用
Mongoose ODM
以及

PugJS 模板引擎的格式和语法,并且在一天左右未能找到问题根源之后我从一开始就完成了本教程,并将代码复制并粘贴到应用程序中的每个单独文件中,但我仍然遇到完全相同的问题。 此论坛上发布了许多有关本教程的问题,但没有一个专门针对此案例。我确实发现这个问题以前曾出现过:作者姓名未渲染

,但这似乎已在 2020 年修复。我在 git Hub 讨论中注意到当时有人担心数据库的状态。考虑到这一点,我删除了 Mongo DB 中的所有表,并用原始数据重新填充它,因为我认为我可能在某个时候损坏了数据。这并没有解决问题,我不知道从这里该去哪里。我觉得如果我只是忽略这个问题,它就不会消失,并且很可能会在以后回来咬我。

我发现,如果我在 #{book.author}

文件中使用

#{book.author.name}

 而不是 
book_list.pug
,则应用程序会从数据库中呈现作者的 
_Id
。这告诉我它必须与虚拟类型 
name
 有关,它是在 
AuthorSchema
 上定义的,但是,这就是我的技能所能达到的程度。我只是对这个堆栈技术不够熟悉,不知道如何解决这个问题。
我的应用程序代码如下。

型号

author.js

const mongoose = require("mongoose"); const Schema = mongoose.Schema; const AuthorSchema = new Schema({ first_name: { type: String, required: true, maxLength: 100 }, family_name: { type: String, required: true, maxLength: 100 }, date_of_birth: { type: Date }, date_of_death: { type: Date }, }); AuthorSchema.virtual("name").get(function () { // To avoid erros where an author doesn't have a family name or first name // we want to make sure we handle the exception by returning an empty string for that case let fullname = ""; if (this.first_name && this.last_name) { fullname = `${this.family_name}, ${this.first_name}`; } return fullname; }); // Virtual for author's URL returns an absolute URL // we'll use this property in our templates whenever we want to get a link for an author AuthorSchema.virtual("url").get(function () { // We don't want to use arrow functions because we'll need to us this object return `/catalog/author/${this._id}`; }); // Export the `Author` model module.exports = mongoose.model("Author", AuthorSchema);

book.js

const mongoose = require("mongoose"); const Schema = mongoose.Schema; const BookSchema = new Schema({ title: { type: String, required: true }, author: { type: Schema.Types.ObjectId, ref: "Author", required: true }, summary: { type: String, required: true }, isbn: { type: String, required: true }, genre: [{ type: Schema.Types.ObjectId, ref: "Genre" }], }); // Virtual for book's URL BookSchema.virtual("url").get(function () { // We don't want to use arrow functions as we'll need the `this` object return `/catalog/book/${this._id}`; }); module.exports = mongoose.model("Book", BookSchema);

控制器

bookController.js

// book controller // The controllers use the `express-async-handler module` which will // catch any exceptions thrown in our route handler functions, // this needs to be installed using `npm install 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"); const author = require("../models/author"); 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, }); }); // Display list of all books. exports.book_list = asyncHandler(async (req, res, next) => { const allBooks = await Book.find({}, "title author") .sort({ title: 1 }) .populate("author") .exec(); res.render("book_list", { title: "Book List", book_list: allBooks }); }); // Display detail page for a specific book. exports.book_detail = asyncHandler(async (req, res, next) => { res.send(`NOT IMPLEMENTED: Book detail: ${req.params.id}`); }); // Display book create form on GET. exports.book_create_get = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Book create GET"); }); // Handle book create on POST. exports.book_create_post = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Book create POST"); }); // Display book delete form on GET. exports.book_delete_get = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Book delete GET"); }); // Handle book delete on POST. exports.book_delete_post = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Book delete POST"); }); // Display book update form on GET. exports.book_update_get = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Book update GET"); }); // Handle book update on POST. exports.book_update_post = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Book update POST"); });

authorController

const Author = require("../models/author"); const asyncHandler = require("express-async-handler"); // Display list of all Authors. exports.author_list = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author list"); }); // Display detail page for a specific Author. exports.author_detail = asyncHandler(async (req, res, next) => { res.send(`NOT IMPLEMENTED: Author detail: ${req.params.id}`); }); // Display Author create form on GET. exports.author_create_get = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author create GET"); }); // Handle Author create on POST. exports.author_create_post = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author create POST"); }); // Display Author delete form on GET. exports.author_delete_get = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author delete GET"); }); // Handle Author delete on POST. exports.author_delete_post = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author delete POST"); }); // Display Author update form on GET. exports.author_update_get = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author update GET"); }); // Handle Author update on POST. exports.author_update_post = asyncHandler(async (req, res, next) => { res.send("NOT IMPLEMENTED: Author update POST"); });

路线

catalog.js

// Import the Express application & create a router const express = require("express"); // The routes are all set up on the router which is then exported const router = express.Router(); // Require controller modules. const book_controller = require("../controllers/bookController"); const author_controller = require("../controllers/authorController"); const genre_controller = require("../controllers/genreController"); const book_instance_controller = require("../controllers/bookinstanceController"); /// BOOK ROUTES /// // GET catalog home page. router.get("/", book_controller.index); // GET request for creating a Book. NOTE This must come before routes that display Book (uses id). router.get("/book/create", book_controller.book_create_get); // POST request for creating Book. router.post("/book/create", book_controller.book_create_post); // GET request to delete Book. router.get("/book/:id/delete", book_controller.book_delete_get); // POST request to delete Book. router.post("/book/:id/delete", book_controller.book_delete_post); // GET request to update Book. router.get("/book/:id/update", book_controller.book_update_get); // POST request to update Book. router.post("/book/:id/update", book_controller.book_update_post); // GET request for one Book. router.get("/book/:id", book_controller.book_detail); // GET request for list of all Book items. router.get("/books", book_controller.book_list); /// AUTHOR ROUTES /// // GET request for creating Author. NOTE This must come before route for id (i.e. display author). router.get("/author/create", author_controller.author_create_get); // POST request for creating Author. router.post("/author/create", author_controller.author_create_post); // GET request to delete Author. router.get("/author/:id/delete", author_controller.author_delete_get); // POST request to delete Author. router.post("/author/:id/delete", author_controller.author_delete_post); // GET request to update Author. router.get("/author/:id/update", author_controller.author_update_get); // POST request to update Author. router.post("/author/:id/update", author_controller.author_update_post); // GET request for one Author. router.get("/author/:id", author_controller.author_detail); // GET request for list of all Authors. router.get("/authors", author_controller.author_list); /// GENRE ROUTES /// // GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id). router.get("/genre/create", genre_controller.genre_create_get); //POST request for creating Genre. router.post("/genre/create", genre_controller.genre_create_post); // GET request to delete Genre. router.get("/genre/:id/delete", genre_controller.genre_delete_get); // POST request to delete Genre. router.post("/genre/:id/delete", genre_controller.genre_delete_post); // GET request to update Genre. router.get("/genre/:id/update", genre_controller.genre_update_get); // POST request to update Genre. router.post("/genre/:id/update", genre_controller.genre_update_post); // GET request for one Genre. router.get("/genre/:id", genre_controller.genre_detail); // GET request for list of all Genre. router.get("/genres", genre_controller.genre_list); /// BOOKINSTANCE ROUTES /// // GET request for creating a BookInstance. NOTE This must come before route that displays BookInstance (uses id). router.get( "/bookinstance/create", book_instance_controller.bookinstance_create_get ); // POST request for creating BookInstance. router.post( "/bookinstance/create", book_instance_controller.bookinstance_create_post ); // GET request to delete BookInstance. router.get( "/bookinstance/:id/delete", book_instance_controller.bookinstance_delete_get ); // POST request to delete BookInstance. router.post( "/bookinstance/:id/delete", book_instance_controller.bookinstance_delete_post ); // GET request to update BookInstance. router.get( "/bookinstance/:id/update", book_instance_controller.bookinstance_update_get ); // POST request to update BookInstance. router.post( "/bookinstance/:id/update", book_instance_controller.bookinstance_update_post ); // GET request for one BookInstance. router.get("/bookinstance/:id", book_instance_controller.bookinstance_detail); // GET request for list of all BookInstance. router.get("/bookinstances", book_instance_controller.bookinstance_list); // Export the router module.exports = router;

index.js

var express = require("express"); var router = express.Router(); /* GET home page. */ // router.get("/", function (req, res, next) { // res.render("index", { title: "Express Yourself!" }); // }); // GET home page. router.get("/", function (req, res) { res.redirect("/catalog"); }); module.exports = router;

浏览量

book_list.pug

extends layout block content h1= title if book_list.length ul each book in book_list li a(href=book.url) #{book.title} | (#{book.author.name}) else p There are no books.

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. h1 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}

布局.pug

doctype html html(lang='en') head title= title meta(charset='utf-8') meta(name='viewport', content='width=device-width, initial-scale=1') link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css", integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N", crossorigin="anonymous") script(src="https://code.jquery.com/jquery-3.5.1.slim.min.js", integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", crossorigin="anonymous") script(src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js", integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+", crossorigin="anonymous") link(rel='stylesheet', href='/stylesheets/style.css') body div(class='container-fluid') div(class='row') div(class='col-sm-2') block sidebar ul(class='sidebar-nav') li a(href='/catalog') Home li a(href='/catalog/books') All books li a(href='/catalog/authors') All authors li a(href='/catalog/genres') All genres li a(href='/catalog/bookinstances') All book-instances li hr li a(href='/catalog/author/create') Create new author li a(href='/catalog/genre/create') Create new genre li a(href='/catalog/book/create') Create new book li a(href='/catalog/bookinstance/create') Create new book instance (copy) div(class='col-sm-10') block content

申请

app.js

const createError = require("http-errors"); const express = require("express"); const path = require("path"); const cookieParser = require("cookie-parser"); const logger = require("morgan"); // Import the routes var indexRouter = require("./routes/index"); var usersRouter = require("./routes/users"); const catalogRouter = require("./routes/catalog"); //Import routes for "catalog" area of site const app = express(); // DB connection // Set up mongoose connection // NB: just for dev purposes. Do this more safely for production const mongoose = require("mongoose"); mongoose.set("strictQuery", false); const mongoDB = "mongodb+srv://username:[email protected]/local_library?retryWrites=true&w=majority"; main().catch((err) => console.log(err)); async function main() { await mongoose.connect(mongoDB); } // view engine setup app.set("views", path.join(__dirname, "views")); app.set("view engine", "pug"); app.use(logger("dev")); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); // make Express serve all the files in the .public directory app.use(express.static(path.join(__dirname, "public"))); app.use("/", indexRouter); app.use("/users", usersRouter); app.use("/catalog", catalogRouter); // Add catalog routes to middleware chain. // catch 404 and forward to error handler app.use(function (req, res, next) { next(createError(404)); }); // error handler app.use(function (err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get("env") === "development" ? err : {}; // render the error page res.status(err.status || 500); res.render("error"); }); module.exports = app;

www

#!/usr/bin/env node /** * Module dependencies. */ var app = require('../app'); var debug = require('debug')('express-locallibrary-tutorial:server'); var http = require('http'); /** * Get port from environment and store in Express. */ var port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ var server = http.createServer(app); /** * Listen on provided port, on all network interfaces. */ server.listen(port); server.on('error', onError); server.on('listening', onListening); /** * Normalize a port into a number, string, or false. */ function normalizePort(val) { var port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; } /** * Event listener for HTTP server "error" event. */ function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } } /** * Event listener for HTTP server "listening" event. */ function onListening() { var addr = server.address(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }

我遗漏了我认为不相关的文件,但如果我遗漏了某些内容,请告诉我,我会发布它。
感谢所有关注此事的人,我很感激所花费的时间。

代码几乎是正确的,只是作者模型中有一个拼写错误,其中使用了
javascript node.js mongodb express pug
1个回答
0
投票
而不是

family_name


例如

if (this.first_name && this.last_name) { ... }

应该是
  if (this.first_name && this.family_name) {
     ...
  }


© www.soinside.com 2019 - 2024. All rights reserved.