创建 BookInstance 表单
本小节演示如何定义一个页面/表单来创建BookInstance
对象。这与我们用来创建Book
对象的表单非常相似。
导入验证和清理方法
打开/controllers/bookinstanceController.js,并在文件顶部添加以下几行
const { body, validationResult } = require("express-validator");
控制器 - 获取路由
在文件顶部,引入Book模块(需要引入,因为每个BookInstance
都与特定的Book
相关联)。
const Book = require("../models/book");
找到导出的bookinstance_create_get()
控制器方法,并将其替换为以下代码。
// Display BookInstance create form on GET.
exports.bookinstance_create_get = asyncHandler(async (req, res, next) => {
const allBooks = await Book.find({}, "title").sort({ title: 1 }).exec();
res.render("bookinstance_form", {
title: "Create BookInstance",
book_list: allBooks,
});
});
控制器获取所有书籍的排序列表(allBooks
),并通过book_list
将其传递给视图bookinstance_form.pug
(以及一个title
)。请注意,当我们第一次显示此表单时,还没有选择任何书籍,因此我们不会将selected_book
变量传递给render()
。因此,selected_book
在模板中将具有undefined
的值。
控制器 - 发布路由
找到导出的bookinstance_create_post()
控制器方法,并将其替换为以下代码。
// Handle BookInstance create on POST.
exports.bookinstance_create_post = [
// Validate and sanitize fields.
body("book", "Book must be specified").trim().isLength({ min: 1 }).escape(),
body("imprint", "Imprint must be specified")
.trim()
.isLength({ min: 1 })
.escape(),
body("status").escape(),
body("due_back", "Invalid date")
.optional({ values: "falsy" })
.isISO8601()
.toDate(),
// Process request after validation and sanitization.
asyncHandler(async (req, res, next) => {
// Extract the validation errors from a request.
const errors = validationResult(req);
// Create a BookInstance object with escaped and trimmed data.
const bookInstance = new BookInstance({
book: req.body.book,
imprint: req.body.imprint,
status: req.body.status,
due_back: req.body.due_back,
});
if (!errors.isEmpty()) {
// There are errors.
// Render form again with sanitized values and error messages.
const allBooks = await Book.find({}, "title").sort({ title: 1 }).exec();
res.render("bookinstance_form", {
title: "Create BookInstance",
book_list: allBooks,
selected_book: bookInstance.book._id,
errors: errors.array(),
bookinstance: bookInstance,
});
return;
} else {
// Data from form is valid
await bookInstance.save();
res.redirect(bookInstance.url);
}
}),
];
此代码的结构和行为与创建其他对象相同。首先,我们验证并清理数据。如果数据无效,则重新显示表单以及用户最初输入的数据和错误消息列表。如果数据有效,则保存新的BookInstance
记录,并将用户重定向到详细信息页面。
视图
创建/views/bookinstance_form.pug并将下面的文本复制到其中。
extends layout
block content
h1=title
form(method='POST')
div.form-group
label(for='book') Book:
select#book.form-control(name='book' required)
option(value='') --Please select a book--
for book in book_list
if selected_book==book._id.toString()
option(value=book._id, selected) #{book.title}
else
option(value=book._id) #{book.title}
div.form-group
label(for='imprint') Imprint:
input#imprint.form-control(type='text' placeholder='Publisher and date information' name='imprint' required value=(undefined===bookinstance ? '' : bookinstance.imprint) )
div.form-group
label(for='due_back') Date when book available:
input#due_back.form-control(type='date' name='due_back' value=(undefined===bookinstance ? '' : bookinstance.due_back_yyyy_mm_dd))
div.form-group
label(for='status') Status:
select#status.form-control(name='status' required)
option(value='') --Please select a status--
each val in ['Maintenance', 'Available', 'Loaned', 'Reserved']
if undefined===bookinstance || bookinstance.status!=val
option(value=val)= val
else
option(value=val selected)= val
button.btn.btn-primary(type='submit') Submit
if errors
ul
for error in errors
li!= error.msg
注意:以上模板硬编码了状态值(维护中、可用等),并且不会“记住”用户输入的值。如果您愿意,可以考虑重新实现列表,从控制器中传入选项数据,并在重新显示表单时设置选定的值。
视图的结构和行为与book_form.pug模板几乎相同,因此我们不会详细介绍。需要注意的一点是,如果我们正在为现有实例填充日期输入,则我们设置“到期归还”日期为bookinstance.due_back_yyyy_mm_dd
的那一行。
input#due_back.form-control(type='date', name='due_back' value=(undefined===bookinstance ? '' : bookinstance.due_back_yyyy_mm_dd))
日期值必须设置为YYYY-MM-DD
格式,因为这是<input>
元素(type="date"
)所期望的,但是日期不是以这种格式存储的,因此我们必须在设置控件中的值之前进行转换。due_back_yyyy_mm_dd()
方法在下一节中添加到BookInstance
模型中。
模型 - 虚拟 due_back_yyyy_mm_dd()
方法
打开定义BookInstanceSchema
模型的文件(models/bookinstance.js)。添加下面显示的due_back_yyyy_mm_dd()
虚拟函数(在due_back_formatted()
虚拟函数之后)
BookInstanceSchema.virtual("due_back_yyyy_mm_dd").get(function () {
return DateTime.fromJSDate(this.due_back).toISODate(); // format 'YYYY-MM-DD'
});
它是什么样子的?
运行应用程序并在浏览器中打开https://127.0.0.1:3000/
。然后选择创建新的图书实例(复制)链接。如果一切设置正确,您的网站应该如下面的屏幕截图所示。提交有效的BookInstance
后,它应该会被保存,并且您将被带到详细信息页面。
后续步骤
- 返回到Express 教程第 6 部分:使用表单。
- 继续第 6 部分的下一小节:删除作者表单。