mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 02:59:04 +08:00
feat: Form.Item no longer need fieldKey
anymore (#32689)
* chore:Update Form to remove fieldKey * fix: fieldKey cache logic * test: Update snapshot * chore: clean up
This commit is contained in:
parent
5a1d8159b1
commit
184402a9bc
@ -1,9 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { useContext } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Field, FormInstance } from 'rc-field-form';
|
||||
import { Field, FormInstance, FieldContext, ListContext } from 'rc-field-form';
|
||||
import { FieldProps } from 'rc-field-form/lib/Field';
|
||||
import FieldContext from 'rc-field-form/lib/FieldContext';
|
||||
import { Meta, NamePath } from 'rc-field-form/lib/interface';
|
||||
import { supportRef } from 'rc-util/lib/ref';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
@ -62,7 +61,7 @@ export interface FormItemProps<Values = any>
|
||||
initialValue?: any;
|
||||
messageVariables?: Record<string, string>;
|
||||
tooltip?: LabelTooltipType;
|
||||
/** Auto passed by List render props. User should not use this. */
|
||||
/** @deprecated No need anymore */
|
||||
fieldKey?: React.Key | React.Key[];
|
||||
}
|
||||
|
||||
@ -86,7 +85,6 @@ function genEmptyMeta(): Meta {
|
||||
function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElement {
|
||||
const {
|
||||
name,
|
||||
fieldKey,
|
||||
noStyle,
|
||||
dependencies,
|
||||
prefixCls: customizePrefixCls,
|
||||
@ -119,6 +117,11 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
|
||||
const prefixCls = getPrefixCls('form', customizePrefixCls);
|
||||
|
||||
// ========================= MISC =========================
|
||||
// Get `noStyle` required info
|
||||
const listContext = React.useContext(ListContext);
|
||||
const fieldKeyPathRef = React.useRef<React.Key[]>();
|
||||
|
||||
// ======================== Errors ========================
|
||||
// >>>>> Collect sub field errors
|
||||
const [subFieldErrors, setSubFieldErrors] = useFrameState<Record<string, FieldError>>({});
|
||||
@ -127,14 +130,27 @@ function FormItem<Values = any>(props: FormItemProps<Values>): React.ReactElemen
|
||||
const [meta, setMeta] = React.useState<Meta>(() => genEmptyMeta());
|
||||
|
||||
const onMetaChange = (nextMeta: Meta & { destroy?: boolean }) => {
|
||||
// This keyInfo is not correct when field is removed
|
||||
// Since origin keyManager no longer keep the origin key anymore
|
||||
// Which means we need cache origin one and reuse when removed
|
||||
const keyInfo = listContext?.getKey(nextMeta.name);
|
||||
|
||||
// Destroy will reset all the meta
|
||||
setMeta(nextMeta.destroy ? genEmptyMeta() : nextMeta);
|
||||
|
||||
// Bump to parent since noStyle
|
||||
if (noStyle && notifyParentMetaChange) {
|
||||
let namePath = nextMeta.name;
|
||||
if (fieldKey !== undefined) {
|
||||
namePath = Array.isArray(fieldKey) ? fieldKey : [fieldKey!];
|
||||
|
||||
if (!nextMeta.destroy) {
|
||||
if (keyInfo !== undefined) {
|
||||
const [fieldKey, restPath] = keyInfo;
|
||||
namePath = [fieldKey, ...restPath];
|
||||
fieldKeyPathRef.current = namePath;
|
||||
}
|
||||
} else {
|
||||
// Use origin cache data
|
||||
namePath = fieldKeyPathRef.current || namePath;
|
||||
}
|
||||
notifyParentMetaChange(nextMeta, namePath);
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import { FormItemPrefixContext } from './context';
|
||||
export interface FormListFieldData {
|
||||
name: number;
|
||||
key: number;
|
||||
fieldKey: number;
|
||||
}
|
||||
|
||||
export interface FormListOperation {
|
||||
@ -43,14 +42,10 @@ const FormList: React.FC<FormListProps> = ({
|
||||
<List {...props}>
|
||||
{(fields, operation, meta) => (
|
||||
<FormItemPrefixContext.Provider value={{ prefixCls, status: 'error' }}>
|
||||
{children(
|
||||
fields.map(field => ({ ...field, fieldKey: field.key })),
|
||||
operation,
|
||||
{
|
||||
{children(fields, operation, {
|
||||
errors: meta.errors,
|
||||
warnings: meta.warnings,
|
||||
},
|
||||
)}
|
||||
})}
|
||||
</FormItemPrefixContext.Provider>
|
||||
)}
|
||||
</List>
|
||||
|
@ -1904,6 +1904,112 @@ exports[`renders ./components/form/demo/dynamic-form-items-complex.md correctly
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/form/demo/dynamic-form-items-no-style.md correctly 1`] = `
|
||||
<form
|
||||
autocomplete="off"
|
||||
class="ant-form ant-form-horizontal"
|
||||
id="dynamic_form_nest_item"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
title="Users"
|
||||
>
|
||||
Users
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-dashed ant-btn-block"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-label="plus"
|
||||
class="anticon anticon-plus"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="plus"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<defs />
|
||||
<path
|
||||
d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"
|
||||
/>
|
||||
<path
|
||||
d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span>
|
||||
Add field
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col ant-form-item-control"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control-input-content"
|
||||
>
|
||||
<button
|
||||
class="ant-btn ant-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
<span>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/form/demo/dynamic-rule.md correctly 1`] = `
|
||||
<form
|
||||
class="ant-form ant-form-horizontal"
|
||||
|
@ -23,7 +23,6 @@ describe('Form.List.NoStyle', () => {
|
||||
<Form.Item
|
||||
{...field}
|
||||
name={[field.name, 'first']}
|
||||
fieldKey={[field.fieldKey, 'first']}
|
||||
rules={[{ required: true }]}
|
||||
noStyle
|
||||
>
|
||||
|
@ -61,7 +61,6 @@ const Demo = () => {
|
||||
{...field}
|
||||
label="Sight"
|
||||
name={[field.name, 'sight']}
|
||||
fieldKey={[field.fieldKey, 'sight']}
|
||||
rules={[{ required: true, message: 'Missing sight' }]}
|
||||
>
|
||||
<Select disabled={!form.getFieldValue('area')} style={{ width: 130 }}>
|
||||
@ -78,7 +77,6 @@ const Demo = () => {
|
||||
{...field}
|
||||
label="Price"
|
||||
name={[field.name, 'price']}
|
||||
fieldKey={[field.fieldKey, 'price']}
|
||||
rules={[{ required: true, message: 'Missing price' }]}
|
||||
>
|
||||
<Input />
|
||||
|
66
components/form/demo/dynamic-form-items-no-style.md
Normal file
66
components/form/demo/dynamic-form-items-no-style.md
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
order: 4.11
|
||||
title:
|
||||
zh-CN: 动态增减嵌套纯字段
|
||||
en-US: Dynamic Form nest pure Items
|
||||
debug: true
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
嵌套 `noStyle` 字段的动态表单示例。
|
||||
|
||||
## en-US
|
||||
|
||||
Nest with `noStyle` field dynamic form.
|
||||
|
||||
```jsx
|
||||
import { Form, Input, Button, Space } from 'antd';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
const Demo = () => {
|
||||
const onFinish = values => {
|
||||
console.log('Received values of form:', values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
|
||||
<Form.Item label="Users">
|
||||
<Form.List name="users">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(field => (
|
||||
<Space key={field.key} style={{ marginBottom: 16 }}>
|
||||
<Form.Item noStyle name={[field.name, 'lastName']} rules={[{ required: true }]}>
|
||||
<Input placeholder="Last Name" />
|
||||
</Form.Item>
|
||||
<Form.Item noStyle name={[field.name, 'firstName']} rules={[{ required: true }]}>
|
||||
<Input placeholder="First Name" />
|
||||
</Form.Item>
|
||||
<MinusCircleOutlined
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
|
||||
Add field
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
@ -7,11 +7,11 @@ title:
|
||||
|
||||
## zh-CN
|
||||
|
||||
嵌套表单字段需要对 `field` 进行拓展,将 `field.name` 和 `field.fieldKey` 应用于控制字段。
|
||||
嵌套表单字段需要对 `field` 进行拓展,将 `field.name` 应用于控制字段。
|
||||
|
||||
## en-US
|
||||
|
||||
Nest dynamic field need extends `field`. Pass `field.name` and `field.fieldKey` to nest item.
|
||||
Nest dynamic field need extends `field`. Pass `field.name` to nest item.
|
||||
|
||||
```jsx
|
||||
import { Form, Input, Button, Space } from 'antd';
|
||||
@ -27,12 +27,11 @@ const Demo = () => {
|
||||
<Form.List name="users">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, fieldKey, ...restField }) => (
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'first']}
|
||||
fieldKey={[fieldKey, 'first']}
|
||||
rules={[{ required: true, message: 'Missing first name' }]}
|
||||
>
|
||||
<Input placeholder="First Name" />
|
||||
@ -40,7 +39,6 @@ const Demo = () => {
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'last']}
|
||||
fieldKey={[fieldKey, 'last']}
|
||||
rules={[{ required: true, message: 'Missing last name' }]}
|
||||
>
|
||||
<Input placeholder="Last Name" />
|
||||
|
@ -124,7 +124,7 @@
|
||||
"rc-dialog": "~8.6.0",
|
||||
"rc-drawer": "~4.4.2",
|
||||
"rc-dropdown": "~3.2.0",
|
||||
"rc-field-form": "~1.21.0",
|
||||
"rc-field-form": "~1.22.0-2",
|
||||
"rc-image": "~5.2.5",
|
||||
"rc-input-number": "~7.3.0",
|
||||
"rc-mentions": "~1.6.1",
|
||||
|
Loading…
Reference in New Issue
Block a user