Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update create author form #24261

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0b86ea0
update create_genre_form doc
ikenk Oct 25, 2024
9e7aa01
update-create_author_form
ikenk Oct 27, 2024
0361fd6
[ko] sync translated content (#24211)
mdn-bot Oct 24, 2024
bf2f288
Update index.md one missing word in french translation (#24210)
hoel44 Oct 24, 2024
ffe7b26
Typofix for Notions de base en HTML: "Comment ..." => "Comme ..." (#2…
FacuBotta Oct 24, 2024
4e39cc2
Fix #23755 (#23964)
SphinxKnight Oct 24, 2024
626f1d0
[ja] sync translated content
hiroya-uga Oct 24, 2024
477c80e
2024/10/02 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
d342e38
2024/10/09 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
93fd572
2024/09/05 時点の英語版に基づき新規翻訳
mfuji09 Oct 20, 2024
9c2f0a6
2024/09/29 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
f4c973d
2024/09/27 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
0300a7c
2024/09/27 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
a07f88b
2024/09/23 時点の英語版に基づき更新 (#24158)
mfuji09 Oct 25, 2024
9ada812
2024/10/11 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
fb126c8
2024/10/16 時点の英語版に基づき更新
mfuji09 Oct 20, 2024
8adde34
2024/04/17 時点の英語版に基づき新規翻訳
mfuji09 Oct 20, 2024
6fdde6f
2024/10/09 時点の英語版に基づき更新 (#24167)
mfuji09 Oct 25, 2024
92309a5
2024/07/26 時点の英語版に基づき新規翻訳
mfuji09 Oct 21, 2024
e977d5d
2024/10/15 時点の英語版に基づき更新
mfuji09 Oct 21, 2024
c45fba9
2024/10/11 時点の英語版に基づき更新
mfuji09 Oct 21, 2024
27956bd
[es] Add missing 'element copy event' page (#24223)
rafael-encinas Oct 25, 2024
f277a06
[zh-cn]: fix sidebar missing for Web/Performance/* (#24224)
Yanko1013 Oct 26, 2024
7d0a56b
[zh-cn]: create doc for HTMLMarqueeElement (#24087)
fuchunhui Oct 26, 2024
13e8e86
2024/10/11 時点の英語版に基づき更新 (#24200)
mfuji09 Oct 26, 2024
4b65bd4
2024/09/03 時点の英語版に基づき更新
mfuji09 Oct 22, 2024
8da5e9d
2024/10/16 時点の英語版に基づき更新
mfuji09 Oct 22, 2024
5819d97
日本語索引項目を追加
mfuji09 Oct 22, 2024
bebabc1
Fix link paths
hiroya-uga Oct 26, 2024
939f7f1
原文にない Compat 情報の削除
ihasq Oct 26, 2024
7ea644c
[ru] improve wording in `Web/HTTP/MIME_types` (#24222)
vbrovenk Oct 26, 2024
e4146d1
zh-cn: sync the translation of "event loop" (#24197)
familyboat Oct 27, 2024
003824b
[ko] 띄어쓰기와 오타 수정 (#24258)
ICE0208 Oct 27, 2024
553534c
[ko] Intl.Locale.prototype.calendar 신규 번역 (#22084)
wisedog Oct 27, 2024
8fae54a
[ko] TypedArray.prototype.filter() 신규 번역 외 (#22136)
wisedog Oct 27, 2024
9563f6f
[ko] Intl.Locale.prototype.getTextInfo() 신규 번역 외 (#22295)
wisedog Oct 27, 2024
335216f
[ko] TypedArray.from() 신규 번역 외 (#22330)
wisedog Oct 27, 2024
ca86272
[ko] DataView.prototype.getFloat16() 신규 번역 외 (#22391)
wisedog Oct 27, 2024
8503fe3
[ko] SharedArrayBuffer.prototype.growable 신규 번역 외 (#22396)
wisedog Oct 27, 2024
25bfa3b
[ko] Iterator.prototype.filter() 신규 번역 (#22881)
wisedog Oct 27, 2024
258a874
ko: add translation for `Element: mousedown event` (#22886)
psst54 Oct 27, 2024
f257c42
[ko] Iterator.prototype.find() 신규 번역 (#22897)
wisedog Oct 27, 2024
72e0ade
[ko] SharedArrayBuffer[Symbol.species] 신규 번역 외 (#22903)
wisedog Oct 27, 2024
65e7b20
[ko] double colon placeholder 번역 (#22925)
givvemee Oct 27, 2024
9eefafe
[ko] accent color 신규 번역 (#23154)
givvemee Oct 27, 2024
850fb01
[ko] ExtendableEvent: waitUntil() 메서드 신규 번역 (#23212)
etoile-j Oct 27, 2024
a2f6264
[ko] css_color_adjustment 신규번역 (#23396)
eun-hak Oct 27, 2024
dc1dbce
[ko] htmlinputelement selectionend 신규 번역 (#23382)
givvemee Oct 27, 2024
c414541
fix: 브라우저 호환성 오타 수정 (#24262)
hochan222 Oct 27, 2024
ce99a81
[ko] api/Navigator/clipboard 신규 번역 (#23599)
rosaceaee Oct 27, 2024
1df7fe2
Bump mdast-util-from-markdown from 2.0.1 to 2.0.2 (#24266)
dependabot[bot] Oct 28, 2024
0c3d8aa
update-create_author_form
ikenk Oct 31, 2024
9f3bc45
Merge branch 'mdn:main' into update-create_author_form
ikenk Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,168 +3,178 @@ title: 创建作者表单
slug: Learn/Server-side/Express_Nodejs/forms/Create_author_form
---

本章节演示,如何为创建作者对象`Author`定义一个页面。
{{LearnSidebar}}

## 导入验证和清理方法
本章节将演示如何定义一个用于创建 `Author` 对象的页面。

为了在种类表单使用 express 验证器,我们必须用 require 导入我们想用的函式。
## 导入验证和修整方法

打开 **/controllers/authorController.js**,并在档案最上方加入底下几行:
与![创建种类表单](/zh-CN/docs/Learn/Server-side/Express_Nodejs/forms/Create_genre_form) 一样,要使用 _express-validator_,我们必须 _require_ 我们想要使用的函数。
ikenk marked this conversation as resolved.
Show resolved Hide resolved

打开 **/controllers/authorController.js**,并将以下代码添加到文件顶部(路由函数上方):

```js
const { body, validationResult } = require("express-validator/check");
const { sanitizeBody } = require("express-validator/filter");
const { body, validationResult } = require("express-validator");
```

## 控制器—get 路由
## 控制器—get 路由

找到导出的 `author_create_get()`控制器方法,并替换为底下代码。这里单纯呈现 **author_form.pug** 视图,传送 `title` 变数
找到导出的 `author_create_get()` 控制器方法并用以下代码替换。此方法会将 `title` 变量传入 `author_form.pug` 视图并渲染

```js
// Display Author create form on GET.
exports.author_create_get = function (req, res, next) {
// 展示 GET 方法获取的创建作者表单
exports.author_create_get = (req, res, next) => {
res.render("author_form", { title: "Create Author" });
};
```

## 控制器—post 路由
## 控制器—post 路由

找到导出的 `author_create_post()` 控制器方法,并替换为底下代码
找到导出的 `author_create_post()` 控制器方法,并将其替换为以下代码

```js
// Handle Author create on POST.
// 处理 POST 方法提交的创建作者表单
exports.author_create_post = [
// Validate fields.
// 验证并且修整字段
body("first_name")
.isLength({ min: 1 })
.trim()
.isLength({ min: 1 })
.escape()
.withMessage("First name must be specified.")
.isAlphanumeric()
.withMessage("First name has non-alphanumeric characters."),
body("family_name")
.isLength({ min: 1 })
.trim()
.isLength({ min: 1 })
.escape()
.withMessage("Family name must be specified.")
.isAlphanumeric()
.withMessage("Family name has non-alphanumeric characters."),
body("date_of_birth", "Invalid date of birth")
.optional({ checkFalsy: true })
.isISO8601(),
.optional({ values: "falsy" })
.isISO8601()
.toDate(),
body("date_of_death", "Invalid date of death")
.optional({ checkFalsy: true })
.isISO8601(),

// Sanitize fields.
sanitizeBody("first_name").trim().escape(),
sanitizeBody("family_name").trim().escape(),
sanitizeBody("date_of_birth").toDate(),
sanitizeBody("date_of_death").toDate(),

// Process request after validation and sanitization.
(req, res, next) => {
// Extract the validation errors from a request.
.optional({ values: "falsy" })
.isISO8601()
.toDate(),

// 在验证和修整完字段后处理请求
asyncHandler(async (req, res, next) => {
// 从请求中提取验证错误
const errors = validationResult(req);

// 使用经过 trim() 和 escape() 处理过的数据创建作者对象
const author = new Author({
first_name: req.body.first_name,
family_name: req.body.family_name,
date_of_birth: req.body.date_of_birth,
date_of_death: req.body.date_of_death,
});

if (!errors.isEmpty()) {
// There are errors. Render form again with sanitized values/errors messages.
// 出现了错误,那么就用修整过的值和错误信息再次渲染一遍表单
res.render("author_form", {
title: "Create Author",
author: req.body,
author: author,
errors: errors.array(),
});
return;
} else {
// Data from form is valid.

// Create an Author object with escaped and trimmed data.
var author = new Author({
first_name: req.body.first_name,
family_name: req.body.family_name,
date_of_birth: req.body.date_of_birth,
date_of_death: req.body.date_of_death,
});
author.save(function (err) {
if (err) {
return next(err);
}
// Successful - redirect to new author record.
res.redirect(author.url);
});
// 表格中的数据有效

// 保存作者信息
await author.save();
// Redirect to new author record.
// 重定向到新的作者记录
res.redirect(author.url);
}
},
}),
];
```

此代码的结构和行为,几乎与创建`Genre`对象完全相同。首先,我们验证并清理数据。如果数据无效,那么我们将重新显示表单,以及用户最初输入的数据,和错误消息列表。如果数据有效,那么我们保存新的作者记录,并将用户重定向到作者详细信息页面。
> [!WARNING]
> 切勿使用 `isAlphanumeric()` 来验证 _name_(正如上面代码所写的那样),因为有许多名字会使用其他字符集。我们在这里这样做是为了演示如何使用验证器,以及如何将其与其他验证器和错误报告进行链式调用。

> [!NOTE]
> 与`Genre` post 处理程序不同,我们不会在保存之前,检查`Author`对象是否已存在。可以说,我们应该这样做,尽管现在我们可以有多个具有相同名称的作者。
此代码的结构和行为几乎与创建 `Genre` 对象一致。首先,我们验证并修整数据。如果数据无效,我们将重新显示表单以及用户最初输入的数据和错误消息列表。如果数据有效,那么我们将保存新的作者记录并将用户重定向到作者详情页面。

与 `Genre` 的 post 处理程序不同,我们不会在保存 `Author` 对象之前检查其是否已经存在。从某种程度上说我们应该这样做,但目前我们可能会有多个同名作者。

验证代码演示了几个新功能:

- 我们可以用菊花链式连接验证器,使用`withMessage()`指定在前一个验证方法失败时,显示的错误消息。这使得在没有大量代码重复的情况下,提供特定的错误消息变得非常容易。
- 我们可以链式调用验证器,使用 `withMessage()` 指定在先前的验证方法失败时需要显示的错误消息。这使得在没有大量代码重复的情况下,提供特定的错误消息变得非常容易。

```js
// Validate fields.
body('first_name').isLength({ min: 1 }).trim().withMessage('First name must be specified.')
.isAlphanumeric().withMessage('First name has non-alphanumeric characters.'),
[
// 验证并修整字段
body("first_name")
.trim()
.isLength({ min: 1 })
.escape()
.withMessage("First name must be specified.")
.isAlphanumeric()
.withMessage("First name has non-alphanumeric characters."),
// …
];
```

- 我们可以使用`optional()`函数,仅在输入字段时运行后续验证(这允许我们验证可选字段)。例如,下面我们检查可选的出生日期是否符合 ISO8601 标准(`checkFalsy` 旗标,表示我们接受空字符串或`null`作为空值)。
- 我们可以使用 `optional()` 函数来保证仅当有字段输入时才去运行后续的验证(这允许我们验证可选字段)。例如,下面我们检查可选的出生日期是否符合 ISO8601 标准(传递的`{ values: "falsy" }`对象意味着我们将接受空字符串或 `null` 作为空值)。
ikenk marked this conversation as resolved.
Show resolved Hide resolved

```js
body('date_of_birth', 'Invalid date of birth').optional({ checkFalsy: true }).isISO8601(),
[
body("date_of_birth", "Invalid date of birth")
.optional({ values: "falsy" })
.isISO8601()
.toDate(),
];
```

- 参数从请求中作为字符串接收。我们可以使用`toDate()`(或`toBoolean()`等)将这些转换为正确的 JavaScript 类型。

```js
sanitizeBody('date_of_birth').toDate(),
```
- 参数以字符串形式从请求中接收。我们可以使用 `toDate()`(或 `toBoolean()`)将它们转换为正确的 JavaScript 类型(如上方验证器链末尾所示)。

## 视图

创建 **/views/author_form.pug** 并复制贴上以下文字
创建 **/views/author_form.pug** 并复制下方文本

```plain
```pug
extends layout

block content
h1=title

form(method='POST' action='')
form(method='POST')
div.form-group
label(for='first_name') First Name:
input#first_name.form-control(type='text' placeholder='First name (Christian) last' name='first_name' required='true' value=(undefined===author ? '' : author.first_name) )
input#first_name.form-control(type='text', placeholder='First name (Christian)' name='first_name' required value=(undefined===author ? '' : author.first_name) )
label(for='family_name') Family Name:
input#family_name.form-control(type='text' placeholder='Family name (surname)' name='family_name' required='true' value=(undefined===author ? '' : author.family_name))
input#family_name.form-control(type='text', placeholder='Family name (Surname)' name='family_name' required value=(undefined===author ? '' : author.family_name))
div.form-group
label(for='date_of_birth') Date of birth:
input#date_of_birth.form-control(type='date' name='date_of_birth' value=(undefined===author ? '' : author.date_of_birth) )
button.btn.btn-primary(type='submit') Submit

if errors
ul
for error in errors
li!= error.msg
```

此视图的结构和行为与**genre_form.pug**模板完全相同,因此我们不再对其进行描述
该视图的结构和行为与 `genre_form.pug` 模板完全相同,因此我们不会再次描述它

> [!NOTE]
> 某些浏览器不支持 input `type=“date”`,因此你不会获得日期选取部件或默认的*`dd/mm/yyyy`*占位符,而是获取一个空的纯文本字段。一种解决方法,是明确添加属性`placeholder='dd/mm/yyyy'`,以便在功能较少的浏览器上,仍然可以获得有关所需文本格式的信息
> 某些浏览器不支持 input 标签的 `type=“date”` 属性,因此你不会获得日期选择器小组件或默认的 `dd/mm/yyyy` 占位符,而是获取一个空的纯文本字段。一种解决方法是显式添加属性 `placeholder='dd/mm/yyyy'`,以便在功能较差的浏览器上仍然可以获得有关所需文本格式的信息

### 自我挑战:加入死亡日期
### 自我挑战:添加死亡日期

上面的模板少了一个输入字段 `date_of_death` 。依照跟生日表单同样的模式,创建此字段
上面的模板缺少用于输入死亡日期 `date_of_death` 的字段。按照与出生日期表单组相同的模式创建字段

## 它看起來像是?

运行本应用,打开浏览器访问网址<http://localhost:3000/>,然后点击创建新作者 Create new author 连结。如果每个东西都设定正确了,你的网站看起应该像底下的截图。在你输入一个值之后,它应该会被储存,并且你将被带到作者详细信息页面
运行本应用,打开浏览器访问网址 `http://localhost:3000/`,然后点击 `Create new author` 链接。如果一切设置正确,你的网站应类似于下方截图。在你输入一个值后,它应该会被保存,并且进入作者详情页面

![Author Create Page - Express Local Library site](locallibary_express_author_create_empty.png)
![作者创建页面——Express 本地图书馆网站](locallibary_express_author_create_empty.png)

> [!NOTE]
> 如果你尝试使用日期的各种输入格式,你可能会发现格式`yyyy-mm-dd`行为不正常。这是因为 JavaScript 将日期字符串,视为包含 0 小时的时间,但另外将该格式的日期字符串(ISO 8601 标准)视为包括 0 小时 UTC 时间,而不是本地时间。如果你的时区在 UTC 以西,则日期显示(即本地)将在你输入的日期之前一天。这是我们在这里没有解决的几个复杂问题之一(例如多字姓和有多个作者的书本)
> 如果你尝试使用各种日期输入格式,你可能会发现格式 `yyyy-mm-dd` 行为不恰当。这是因为 JavaScript 中的日期字符串包含了 0 小时这个时间,而且还将该格式的日期字符串(ISO 8601 标准)视为包括 0 小时的 UTC 时间,而不是本地时间。如果你的时区在 UTC 以西,则本地日期显示将会是你输入的日期的前一天。这是我们在此没有解决的几个复杂问题(例如多字姓氏和多作者书籍)之一

## 下一步

Expand Down
Loading