gutenbergdocs/docs/how-to-guides/data-basics/4-building-a-create-page-form.md
2025-10-22 01:40:18 +08:00

395 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 构建创建页面表单
在[上一章节](/docs/how-to-guides/data-basics/3-building-an-edit-form.md)中我们创建了*编辑页面*功能,本章节我们将新增*创建页面*功能。以下是我们即将构建功能的预览:
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/create-form/create-form-with-text.png)
### 步骤一:添加“创建新页面”按钮
首先我们构建一个用于显示创建页面表单的按钮,这与我们在[第三章节](/docs/how-to-guides/data-basics/3-building-an-edit-form.md)构建的编辑按钮类似:
```js
import { useDispatch } from '@wordpress/data';
import { Button, Modal, TextControl } from '@wordpress/components';
function CreatePageButton() {
const [isOpen, setOpen] = useState( false );
const openModal = () => setOpen( true );
const closeModal = () => setOpen( false );
return (
<>
<Button onClick={ openModal } variant="primary">
创建新页面
</Button>
{ isOpen && (
<Modal onRequestClose={ closeModal } title="创建新页面">
<CreatePageForm
onCancel={ closeModal }
onSaveFinished={ closeModal }
/>
</Modal>
) }
</>
);
}
function CreatePageForm() {
// 暂时留空
return <div/>;
}
```
很好!现在让`MyFirstApp`显示我们全新的按钮:
```js
function MyFirstApp() {
// ...
return (
<div>
<div className="list-controls">
<SearchControl onChange={ setSearchTerm } value={ searchTerm }/>
<CreatePageButton/>
</div>
<PagesList hasResolved={ hasResolved } pages={ pages }/>
</div>
);
}
```
最终效果如下所示:
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/create-form/create-button.png)
### 步骤二:提取受控页面表单
按钮就位后,我们可以全力构建表单。本教程重点在于数据管理,因此不会构建完整的页面编辑器。表单将仅包含一个字段:文章标题。
幸运的是,我们在[第三章节](/docs/how-to-guides/data-basics/3-building-an-edit-form.md)构建的`EditPageForm`已经实现了80%的功能。大部分用户界面已就绪,我们将在`CreatePageForm`中复用这些组件。首先将表单UI提取为独立组件
```js
function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
// ...
return (
<PageForm
title={ page.title }
onChangeTitle={ handleChange }
hasEdits={ hasEdits }
lastError={ lastError }
isSaving={ isSaving }
onCancel={ onCancel }
onSave={ handleSave }
/>
);
}
function PageForm( { title, onChangeTitle, hasEdits, lastError, isSaving, onCancel, onSave } ) {
return (
<div className="my-gutenberg-form">
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
label="页面标题:"
value={ title }
onChange={ onChangeTitle }
/>
{ lastError ? (
<div className="form-error">错误{ lastError.message }</div>
) : (
false
) }
<div className="form-buttons">
<Button
onClick={ onSave }
variant="primary"
disabled={ !hasEdits || isSaving }
>
{ isSaving ? (
<>
<Spinner/>
保存中
</>
) : '保存' }
</Button>
<Button
onClick={ onCancel }
variant="tertiary"
disabled={ isSaving }
>
取消
</Button>
</div>
</div>
);
}
```
这段代码的质量优化不应改变应用程序的任何功能。让我们尝试编辑页面来确认:
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/create-form/edit-page-form.png)
很好!编辑表单依然存在,现在我们有了构建新`CreatePageForm`的基础模块。
### 整合所有代码
以下是本章节构建的全部内容:
```js
function CreatePageForm( { onCancel, onSaveFinished } ) {
const [title, setTitle] = useState();
const { lastError, isSaving } = useSelect(
( select ) => ( {
lastError: select( coreDataStore )
.getLastEntitySaveError( 'postType', 'page' ),
isSaving: select( coreDataStore )
.isSavingEntityRecord( 'postType', 'page' ),
} ),
[]
);
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord(
'postType',
'page',
{ title, status: 'publish' }
);
if ( savedRecord ) {
onSaveFinished();
}
};
return (
<PageForm
title={ title }
onChangeTitle={ setTitle }
hasEdits={ !!title }
onSave={ handleSave }
lastError={ lastError }
onCancel={ onCancel }
isSaving={ isSaving }
/>
);
}
function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
const { page, lastError, isSaving, hasEdits } = useSelect(
( select ) => ( {
page: select( coreDataStore ).getEditedEntityRecord( 'postType', 'page', pageId ),
lastError: select( coreDataStore ).getLastEntitySaveError( 'postType', 'page', pageId ),
isSaving: select( coreDataStore ).isSavingEntityRecord( 'postType', 'page', pageId ),
hasEdits: select( coreDataStore ).hasEditsForEntityRecord( 'postType', 'page', pageId ),
} ),
[pageId]
);
const { saveEditedEntityRecord, editEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEditedEntityRecord( 'postType', 'page', pageId );
if ( savedRecord ) {
onSaveFinished();
}
};
const handleChange = ( title ) => editEntityRecord( 'postType', 'page', page.id, { title } );
return (
<PageForm
title={ page.title }
onChangeTitle={ handleChange }
hasEdits={ hasEdits }
lastError={ lastError }
isSaving={ isSaving }
onCancel={ onCancel }
onSave={ handleSave }
/>
);
}
function PageForm( { title, onChangeTitle, hasEdits, lastError, isSaving, onCancel, onSave } ) {
return (
<div className="my-gutenberg-form">
<TextControl
__nextHasNoMarginBottom
__next40pxDefaultSize
label="页面标题:"
value={ title }
onChange={ onChangeTitle }
/>
{ lastError ? (
<div className="form-error">错误{ lastError.message }</div>
) : (
false
) }
<div className="form-buttons">
<Button
onClick={ onSave }
variant="primary"
disabled={ !hasEdits || isSaving }
>
{ isSaving ? (
<>
<Spinner/>
保存中
</>
) : '保存' }
</Button>
<Button
onClick={ onCancel }
variant="tertiary"
disabled={ isSaving }
>
取消
</Button>
</div>
</div>
);
}
```
现在只需刷新页面即可体验表单功能:
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/create-form/create-form-with-text.png)
## 后续步骤
* **下一章节:** [添加删除按钮](/docs/how-to-guides/data-basics/5-adding-a-delete-button.md)
* **上一章节:** [构建编辑表单](/docs/how-to-guides/data-basics/3-building-an-edit-form.md)
* (可选)在 block-development-examples 代码库中查看[完整应用](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/data-basics-59c8f8)
### 步骤三构建CreatePageForm组件
`CreatePageForm`组件只需提供渲染`PageForm`组件所需的七个属性:
* 标题
* 标题变更处理函数
* 编辑状态标识
* 最后错误信息
* 保存状态标识
* 取消处理函数
* 保存处理函数
具体实现如下:
#### 标题、标题变更处理、编辑状态
`EditPageForm`组件更新并保存的是Redux状态中已存在的实体记录因此我们依赖`editedEntityRecords`选择器。
而`CreatePageForm`不存在预先的实体记录只有空表单。用户输入的内容仅存在于本地表单可通过React的`useState`钩子进行跟踪:
```js
function CreatePageForm( { onCancel, onSaveFinished } ) {
const [title, setTitle] = useState();
const handleChange = ( title ) => setTitle( title );
return (
<PageForm
title={ title }
onChangeTitle={ setTitle }
hasEdits={ !!title }
{ /* 其他属性 */ }
/>
);
}
```
#### 保存处理、取消处理
在`EditPageForm`中,我们通过`saveEditedEntityRecord('postType', 'page', pageId )`操作保存Redux状态中的编辑内容。
但`CreatePageForm`既无Redux状态中的编辑内容也无pageId。此时需要调用的是[`saveEntityRecord`](https://developer.wordpress.org/block-editor/reference-guides/data/data-core/#saveentityrecord)操作名称中不含Edited它接收的是代表新实体记录的对象而非pageId。
传递给`saveEntityRecord`的数据会通过POST请求发送到对应REST API接口。例如执行以下操作
```js
saveEntityRecord( 'postType', 'page', { title: "测试页面" } );
```
将向[WordPress页面REST API接口](/wp/v2/pages)发起POST请求请求体中包含单个字段`title=测试页面`。
现在我们将其应用到`CreatePageForm`
```js
function CreatePageForm( { onSaveFinished, onCancel } ) {
// ...
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord(
'postType',
'page',
{ title }
);
if ( savedRecord ) {
onSaveFinished();
}
};
return (
<PageForm
{ /* 其他属性 */ }
onSave={ handleSave }
onCancel={ onCancel }
/>
);
}
```
还需注意:新建页面默认不会被`PagesList`获取。根据REST API文档`/wp/v2/pages`接口在创建POST请求时默认生成`status=draft`的页面但返回GET请求的是`status=publish`的页面。解决方案是显式传递status参数
```js
function CreatePageForm( { onSaveFinished, onCancel } ) {
// ...
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord(
'postType',
'page',
{ title, status: 'publish' }
);
if ( savedRecord ) {
onSaveFinished();
}
};
return (
<PageForm
{ /* 其他属性 */ }
onSave={ handleSave }
onCancel={ onCancel }
/>
);
}
```
请将此更改应用到本地的`CreatePageForm`组件,接下来处理剩余两个属性。
#### 最后错误、保存状态
`EditPageForm`通过`getLastEntitySaveError`和`isSavingEntityRecord`选择器获取错误和进度信息,两者都传递三个参数:`( 'postType', 'page', pageId )`。
但`CreatePageForm`没有pageId参数。此时可省略pageId参数来获取未指定ID的实体记录信息即新建记录。`useSelect`调用与`EditPageForm`非常相似:
```js
function CreatePageForm( { onCancel, onSaveFinished } ) {
// ...
const { lastError, isSaving } = useSelect(
( select ) => ( {
// 注意省略了pageId参数
lastError: select( coreDataStore )
.getLastEntitySaveError( 'postType', 'page' ),
// 注意省略了pageId参数
isSaving: select( coreDataStore )
.isSavingEntityRecord( 'postType', 'page' ),
} ),
[]
);
// ...
return (
<PageForm
{ /* 其他属性 */ }
lastError={ lastError }
isSaving={ isSaving }
/>
);
}
```
大功告成!以下是我们新表单的实际运行效果:
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/create-form/create-saving.png)
![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/create-form/created-item.png)