diff --git a/.dumi/theme/plugin.ts b/.dumi/theme/plugin.ts index b6a75b2482..e3c3b59865 100644 --- a/.dumi/theme/plugin.ts +++ b/.dumi/theme/plugin.ts @@ -39,6 +39,13 @@ export const getHash = (str: string, length = 8) => class AntdReactTechStack extends ReactTechStack { // eslint-disable-next-line class-methods-use-this generatePreviewerProps(...[props, opts]: any) { + props.pkgDependencyList = { ...devDependencies, ...dependencies }; + props.jsx ??= ''; + + if (opts.type === 'code-block') { + props.jsx = opts?.entryPointCode ? sylvanas.parseText(opts.entryPointCode) : ''; + } + if (opts.type === 'external') { // try to find md file with the same name as the demo tsx file const locale = opts.mdAbsPath.match(/index\.([a-z-]+)\.md$/i)?.[1]; @@ -48,7 +55,6 @@ class AntdReactTechStack extends ReactTechStack { const codePath = opts.fileAbsPath!.replace(/\.\w+$/, '.tsx'); const code = fs.existsSync(codePath) ? fs.readFileSync(codePath, 'utf-8') : ''; - props.pkgDependencyList = { ...devDependencies, ...dependencies }; props.jsx = sylvanas.parseText(code); if (md) { diff --git a/docs/blog/form-names.en-US.md b/docs/blog/form-names.en-US.md index 4a5f56327c..6553fcb9c6 100644 --- a/docs/blog/form-names.en-US.md +++ b/docs/blog/form-names.en-US.md @@ -75,33 +75,47 @@ rules={[{ ## Final Result -```tsx +```tsx | demo +/** + * defaultShowCode: true + */ import React from 'react'; import type { FormItemProps } from 'antd'; -import { Cascader, Form } from 'antd'; +import { Button, Cascader, DatePicker, Form as OriginForm } from 'antd'; +import dayjs from 'dayjs'; -export const AggregateFormItem = ( - props: FormItemProps & { names?: FormItemProps>['name'][] }, -) => { - const form = Form.useFormInstance(); +interface AggregateProps extends FormItemProps { + names?: FormItemProps['name'][]; +} + +const Aggregate = (props: AggregateProps) => { + const form = OriginForm.useFormInstance(); const { names = [], rules = [], ...rest } = props; const [firstName, ...resetNames] = names; + return ( <> - ({ value: names.map((name) => form.getFieldValue(name)) })} + getValueProps={() => { + const value = names.map((name) => form.getFieldValue(name)); + if (value.every((v) => v === undefined)) { + return undefined; + } + return { value }; + }} getValueFromEvent={(values) => { // Set the form store values for names - form.setFields(names.map((name, index) => ({ name, value: values[index] }))); - return values[0]; + const fieldData = names.map((name, index) => ({ name, value: values?.[index] })); + form.setFields(fieldData); + return values?.[0]; }} - rules={rules.map((thisRule) => { - if (typeof thisRule === 'object') { + rules={rules.map((rule) => { + if (typeof rule === 'object' && rule) { return { - ...thisRule, + ...rule, transform: () => { // Set the values of the names fields for the rule value const values = names.map((name) => form.getFieldValue(name)); @@ -109,39 +123,70 @@ export const AggregateFormItem = ( }, }; } - return thisRule; + return rule; })} {...rest} /> {/* Bind other fields so they can getFieldValue to get values and setFields to set values */} {resetNames.map((name) => ( - + ))} ); }; -const data = { province: 'Beijing', city: 'Haidian' }; -const options = [ - { value: 'zhejiang', label: 'Zhejiang', children: [{ value: 'hangzhou', label: 'Hangzhou' }] }, - { value: 'jiangsu', label: 'Jiangsu', children: [{ value: 'nanjing', label: 'Nanjing' }] }, -]; -const createUser = (values) => console.log(values); +const data = { + province: 'Beijing', + city: 'Haidian', + startTime: dayjs(), + endTime: dayjs().add(1, 'month'), +}; -export const Demo = () => ( -
{ - createUser(values); - }} - > - +const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [{ value: 'hangzhou', label: 'Hangzhou' }], + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [{ value: 'nanjing', label: 'Nanjing' }], + }, +]; + +const Form = Object.assign(OriginForm, { Aggregate }); + +export default () => ( + console.log(value)}> + - + + + + + + + {/* Similarly, it also applies */} + + + + + + + ); ``` ## Summary -By doing so, we have implemented a feature that allows for operating multiple `names` within a `Form.Item`, making the form logic clearer and easier to maintain. Additionally, there are some edge cases in this example that have not been considered. For instance, `setFields([{ name:'city', value:'nanjing' }])` will not update the selected value of `Cascader`. To achieve a refresh effect, you need to add `Form.useWatch(values => resetNames.map(name => get(values, name)), form);`. Feel free to explore more edge cases and handle them as needed. +By doing so, we have implemented a feature that allows for operating multiple `names` within a `Form.Item`, making the form logic clearer and easier to maintain. + +In addition to the `Cascader` example in the text, it is also applicable to components such as `DatePicker.RangePicker`. In other words, this method can be used in any scenario that requires the aggregation of multiple fields. + +Additionally, there are some edge cases in this example that have not been considered. For instance, `setFields([{ name:'city', value:'nanjing' }])` will not update the selected value of `Cascader`. To achieve a refresh effect, you need to add `Form.useWatch(values => resetNames.map(name => get(values, name)), form);`. + +Feel free to explore more edge cases and handle them as needed. diff --git a/docs/blog/form-names.zh-CN.md b/docs/blog/form-names.zh-CN.md index 31cc6d7671..0c76fb47a4 100644 --- a/docs/blog/form-names.zh-CN.md +++ b/docs/blog/form-names.zh-CN.md @@ -75,33 +75,47 @@ rules={[{ ## 最终效果 -```tsx +```tsx | demo +/** + * defaultShowCode: true + */ import React from 'react'; import type { FormItemProps } from 'antd'; -import { Cascader, Form } from 'antd'; +import { Button, Cascader, DatePicker, Form as OriginForm } from 'antd'; +import dayjs from 'dayjs'; -export const AggregateFormItem = ( - props: FormItemProps & { names?: FormItemProps>['name'][] }, -) => { - const form = Form.useFormInstance(); +interface AggregateProps extends FormItemProps { + names?: FormItemProps['name'][]; +} + +const Aggregate = (props: AggregateProps) => { + const form = OriginForm.useFormInstance(); const { names = [], rules = [], ...rest } = props; const [firstName, ...resetNames] = names; + return ( <> - ({ value: names.map((name) => form.getFieldValue(name)) })} + getValueProps={() => { + const value = names.map((name) => form.getFieldValue(name)); + if (value.every((v) => v === undefined)) { + return undefined; + } + return { value }; + }} getValueFromEvent={(values) => { // 将 form store 分别设置给 names - form.setFields(names.map((name, index) => ({ name, value: values[index] }))); - return values[0]; + const fieldData = names.map((name, index) => ({ name, value: values?.[index] })); + form.setFields(fieldData); + return values?.[0]; }} - rules={rules.map((thisRule) => { - if (typeof thisRule === 'object') { + rules={rules.map((rule) => { + if (typeof rule === 'object' && rule) { return { - ...thisRule, + ...rule, transform: () => { // 将 names 字段的值设置给 rule value const values = names.map((name) => form.getFieldValue(name)); @@ -109,39 +123,70 @@ export const AggregateFormItem = ( }, }; } - return thisRule; + return rule; })} {...rest} /> {/* 绑定其他字段,使其可以 getFieldValue 获取值、setFields 设置值 */} {resetNames.map((name) => ( - + ))} ); }; -const data = { province: 'Beijing', city: 'Haidian' }; -const options = [ - { value: 'zhejiang', label: 'Zhejiang', children: [{ value: 'hangzhou', label: 'Hangzhou' }] }, - { value: 'jiangsu', label: 'Jiangsu', children: [{ value: 'nanjing', label: 'Nanjing' }] }, -]; -const createUser = (values) => console.log(values); +const data = { + province: 'Beijing', + city: 'Haidian', + startTime: dayjs(), + endTime: dayjs().add(1, 'month'), +}; -export const Demo = () => ( -
{ - createUser(values); - }} - > - +const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [{ value: 'hangzhou', label: 'Hangzhou' }], + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [{ value: 'nanjing', label: 'Nanjing' }], + }, +]; + +const Form = Object.assign(OriginForm, { Aggregate }); + +export default () => ( + console.log(value)}> + - + + + + + + + {/* 同理,也适用于 DatePicker.RangePicker */} + + + + + + +
); ``` ## 总结 -通过这种方式,我们实现了一个可以在 `Form.Item` 中操作多个 `name` 的功能,使得表单逻辑更加清晰和易于维护。另外此示例还有些边界场景没有考虑,比如 `setFields([{ name:'city' value:'nanjing' }])` 不会更新 `Cascader` 选中的值,需要增加 `Form.useWatch(values => resetNames.map(name => get(values, name)), form);` 达到刷新效果等。更多的边界问题就交给你去试试吧~ +通过这种方式,我们实现了一个可以在 `Form.Item` 中操作多个 `name` 的功能,使得表单逻辑更加清晰和易于维护。 + +除了文中的 `Cascader` 示例,同样适用于 `DatePicker.RangePicker` 等组件。换句话说,只要是需要聚合多个字段的场景,都可以使用这种方式。 + +另外此示例还有些边界场景没有考虑,比如 `setFields([{ name:'city' value:'nanjing' }])` 不会更新 `Cascader` 选中的值,需要增加 `Form.useWatch(values => resetNames.map(name => get(values, name)), form);` 达到刷新效果等。 + +更多的边界问题就交给你去试试吧~