This is page 33 of 35. Use http://codebase.md/alibaba/formily?page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /docs/guide/advanced/validate.md: -------------------------------------------------------------------------------- ```markdown # Form Validation Formily's form validation uses the extremely powerful and flexible @formily/validator validation engine. There are two main scenarios for validation: - Markup(JSON) Schema scene protocol verification property verification, using JSON Schema's own verification property and x-validator property to achieve verification - Pure JSX scene verification properties, use validator property to achieve verification At the same time, we can also implement linkage verification in effects or x-reactions/reactions Specific rule verification document reference [FieldValidator](https://core.formilyjs.org/api/models/field#fieldvalidator) Form validation is an important part of optimizing user experience and ensuring data accuracy in forms. Formily provides various validation methods, including built-in rule validation, built-in format validation, and custom rule validation. In the following sections, we will introduce these validation methods one by one. ## Built-in rule check Built-in rule validation refers to the common validation rules provided by Formily, such as required, max, min, len, enum, const, multipleOf, etc. These rules can be described using JSON Schema properties or the x-validator property. Formily supports multiple ways of writing built-in rules and it is recommended for teams to establish internal conventions based on their usage habits. #### Markup Schema Use Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, NumberPicker, }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="required_1" title="Required" required x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="required_2" title="Required" x-validator={{ required: true }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="required_3" title="Required" x-validator={[{ required: true }]} x-component="Input" x-decorator="FormItem" /> <SchemaField.Number name="max_1" title="Maximum value (>5 error)" maximum={5} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="max_2" title="Maximum value (>5 error)" x-validator={{ maximum: 5 }} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="max_3" title="Maximum value (>5 error)" x-validator={[{ maximum: 5 }]} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="max_4" title="Maximum value (>=5 error)" exclusiveMaximum={5} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="max_5" title="Maximum value (>=5 error)" x-validator={{ exclusiveMaximum: 5 }} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="max_6" title="Maximum value (>=5 error)" x-validator={[{ exclusiveMaximum: 5 }]} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="min_1" title="Minimum value (<5 error)" minimum={5} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="min_2" title="Minimum value (<5 error)" x-validator={{ minimum: 5 }} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="min_3" title="Minimum value (<5 error)" x-validator={[{ minimum: 5 }]} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="min_4" title="Minimum value (<=5 error)" exclusiveMinimum={5} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="min_5" title="Minimum value (<=5 error)" x-validator={{ exclusiveMinimum: 5 }} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.Number name="min_6" title="Minimum value (<=5 error)" x-validator={[{ exclusiveMinimum: 5 }]} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.String name="length_1" title="Length is 5" x-validator={{ len: 5 }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="length_2" title="Length is 5" x-validator={[{ len: 5 }]} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="maxlength_1" title="Maximum length is 5" maxLength={5} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="maxlength_2" title="Maximum length is 5" x-validator={{ max: 5 }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="maxlength_3" title="Maximum length is 5" x-validator={[{ max: 5 }]} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="maxlength_4" title="Minimum length is 5" minLength={5} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="maxlength_5" title="Minimum length is 5" x-validator={{ min: 5 }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="maxlength_6" title="Minimum length is 5" x-validator={[{ min: 5 }]} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="whitespace" title="Exclude pure whitespace characters" x-validator={[{ whitespace: true }]} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="enum" title="Enumeration match" x-validator={[{ enum: ['1', '2', '3'] }]} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="const" title="Constant match" const="123" x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="multipleOf" title="Divisible match" multipleOf={2} x-component="NumberPicker" x-decorator="FormItem" /> </SchemaField> </Form> ) ``` #### JSON Schema Use Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, NumberPicker, }, }) const schema = { type: 'object', properties: { required_1: { name: 'required_1', title: 'Required', type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, required_2: { name: 'required_2', title: 'Required', type: 'string', 'x-validator': { required: true, }, 'x-decorator': 'FormItem', 'x-component': 'Input', }, required_3: { name: 'required_3', title: 'Required', type: 'string', 'x-validator': [ { required: true, }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, max_1: { name: 'max_1', title: 'Maximum value (>5 error)', type: 'number', maximum: 5, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, max_2: { name: 'max_2', title: 'Maximum value (>5 error)', type: 'number', 'x-validator': { maximum: 5, }, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, max_3: { name: 'max_3', title: 'Maximum value (>5 error)', type: 'number', 'x-validator': [ { maximum: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, max_4: { name: 'max_4', title: 'Maximum value (>=5 error))', type: 'number', exclusiveMaximum: 5, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, max_5: { name: 'max_5', title: 'Maximum value (>=5 error))', type: 'number', 'x-validator': { exclusiveMaximum: 5, }, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, max_6: { name: 'max_6', title: 'Maximum value (>=5 error))', type: 'number', 'x-validator': [ { exclusiveMaximum: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, min_1: { name: 'min_1', title: 'Minimum value (<5 error))', type: 'number', minimum: 5, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, min_2: { name: 'min_2', title: 'Minimum value (<5 error))', type: 'number', 'x-validator': { minimum: 5, }, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, min_3: { name: 'min_3', title: 'Minimum value (<5 error))', type: 'string', 'x-validator': [ { minimum: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, min_4: { name: 'min_4', title: 'Minimum value (<=5 error))', type: 'number', exclusiveMinimum: 5, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, min_5: { name: 'min_5', title: 'Minimum value (<=5 error))', type: 'number', 'x-validator': { exclusiveMinimum: 5, }, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, min_6: { name: 'min_6', title: 'Minimum value (<=5 error))', type: 'number', 'x-validator': [ { exclusiveMinimum: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, length_1: { name: 'length_1', title: 'Length is 5', type: 'string', 'x-validator': { len: 5, }, 'x-decorator': 'FormItem', 'x-component': 'Input', }, length_2: { name: 'length_2', title: 'Length is 5', type: 'string', 'x-validator': [ { len: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, maxlength_1: { name: 'maxlength_1', title: 'Maximum length is 5', type: 'string', maxLength: 5, 'x-decorator': 'FormItem', 'x-component': 'Input', }, maxlength_2: { name: 'maxlength_2', title: 'Maximum length is 5', type: 'string', 'x-validator': { max: 5, }, 'x-decorator': 'FormItem', 'x-component': 'Input', }, maxlength_3: { name: 'maxlength_3', title: 'Maximum length is 5', type: 'string', 'x-validator': [ { max: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, minlength_1: { name: 'minlength_1', title: 'Minimum length is 5', type: 'string', minLength: 5, 'x-decorator': 'FormItem', 'x-component': 'Input', }, minlength_2: { name: 'minlength_2', title: 'Minimum length is 5', type: 'string', 'x-validator': { min: 5, }, 'x-decorator': 'FormItem', 'x-component': 'Input', }, minlength_3: { name: 'minlength_3', title: 'Minimum length is 5', type: 'string', 'x-validator': [ { min: 5, }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, whitespace: { name: 'whitespace', title: 'Exclude pure whitespace characters', type: 'string', 'x-validator': [ { whitespace: true, }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, enum: { name: 'enum', title: 'Enumeration match', type: 'string', 'x-validator': [ { enum: ['1', '2', '3'], }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, const: { name: 'const', title: 'Constant match', type: 'string', const: '123', 'x-decorator': 'FormItem', 'x-component': 'Input', }, multipleOf: { name: 'multipleOf', title: 'Divisible match', type: 'string', multipleOf: 2, 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', }, }, } export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Case ```tsx import React from 'react' import { createForm } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' const form = createForm() export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <Field name="required_1" title="Required" required component={[Input]} decorator={[FormItem]} /> <Field name="required_2" title="Required" validator={{ required: true }} component={[Input]} decorator={[FormItem]} /> <Field name="required_3" title="Required" validator={[{ required: true }]} component={[Input]} decorator={[FormItem]} /> <Field name="max_1" title="Maximum value (>5 error)" validator={{ maximum: 5 }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="max_2" title="Maximum value (>5 error)" validator={[{ maximum: 5 }]} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="max_3" title="Maximum value (>=5 error)" validator={{ exclusiveMaximum: 5 }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="max_4" title="Maximum value (>=5 error)" validator={[{ exclusiveMaximum: 5 }]} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="min_1" title="Minimum value (<5 error)" validator={{ minimum: 5 }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="min_2" title="Minimum value (<5 error)" validator={[{ minimum: 5 }]} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="min_3" title="Minimum value (<=5 error)" validator={{ exclusiveMinimum: 5 }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="min_4" title="Minimum value (<=5 error)" validator={[{ exclusiveMinimum: 5 }]} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="length_1" title="Length is 5" validator={{ len: 5 }} component={[Input]} decorator={[FormItem]} /> <Field name="length_2" title="Length is 5" validator={[{ len: 5 }]} component={[Input]} decorator={[FormItem]} /> <Field name="maxlength_1" title="Maximum length is 5" validator={{ max: 5 }} component={[Input]} decorator={[FormItem]} /> <Field name="maxlength_2" title="Maximum length is 5" validator={[{ max: 5 }]} component={[Input]} decorator={[FormItem]} /> <Field name="minlength_1" title="Minimum length is 5" validator={{ min: 5 }} component={[Input]} decorator={[FormItem]} /> <Field name="minlength_2" title="Minimum length is 5" validator={[{ min: 5 }]} component={[Input]} decorator={[FormItem]} /> <Field name="whitespace" title="Exclude pure whitespace characters" validator={[{ whitespace: true }]} component={[Input]} decorator={[FormItem]} /> </Form> ) ``` ## Built-in Format Verification #### Markup Schema Cases ```tsx import React, { Fragment } from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) const renderFormat = (format: string, key: number) => { return ( <Fragment key={key}> <SchemaField.String name={`${format}_1`} title={`${format} format`} format={format} required x-component="Input" x-decorator="FormItem" /> <SchemaField.String name={`${format}_2`} title={`${format} format`} required x-validator={format} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name={`${format}_3`} title={`${format} format`} required x-validator={{ format }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name={`${format}_4`} title={`${format} format`} required x-validator={[format]} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name={`${format}_5`} title={`${format} format`} required x-validator={[{ format }]} x-component="Input" x-decorator="FormItem" /> </Fragment> ) } const FORMATS = [ 'url', 'email', 'phone', 'ipv6', 'ipv4', 'number', 'integer', 'qq', 'idcard', 'money', 'zh', 'date', 'zip', ] export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField>{FORMATS.map(renderFormat)}</SchemaField> </Form> ) ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const schema = { type: 'object', properties: {}, } const FORMATS = [ 'url', 'email', 'phone', 'ipv6', 'ipv4', 'number', 'integer', 'qq', 'idcard', 'money', 'zh', 'date', 'zip', ] FORMATS.forEach((key) => { Object.assign(schema.properties, { [`${key}_1`]: { title: `${key} format`, type: 'string', required: true, format: key, 'x-decorator': 'FormItem', 'x-component': 'Input', }, [`${key}_2`]: { title: `${key} format`, type: 'string', required: true, 'x-validator': key, 'x-decorator': 'FormItem', 'x-component': 'Input', }, [`${key}_3`]: { title: `${key} format`, type: 'string', required: true, 'x-validator': { format: key, }, 'x-decorator': 'FormItem', 'x-component': 'Input', }, [`${key}_4`]: { title: `${key} format`, type: 'string', required: true, 'x-validator': [key], 'x-decorator': 'FormItem', 'x-component': 'Input', }, [`${key}_5`]: { title: `${key} format`, type: 'string', required: true, 'x-validator': [ { format: key, }, ], 'x-decorator': 'FormItem', 'x-component': 'Input', }, }) }) const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Cases ```tsx import React, { Fragment } from 'react' import { createForm } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const renderFormat = (format: string, key: number) => { return ( <Fragment key={key}> <Field name={`${format}_1`} title={`${format} format`} required validator={format} component={[Input]} decorator={[FormItem]} /> <Field name={`${format}_2`} title={`${format} format`} required validator={{ format }} component={[Input]} decorator={[FormItem]} /> <Field name={`${format}_3`} title={`${format} format`} required validator={[format]} component={[Input]} decorator={[FormItem]} /> <Field name={`${format}_4`} title={`${format} format`} required validator={[{ format }]} component={[Input]} decorator={[FormItem]} /> </Fragment> ) } const FORMATS = [ 'url', 'email', 'phone', 'ipv6', 'ipv4', 'number', 'integer', 'qq', 'idcard', 'money', 'zh', 'date', 'zip', ] export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> {FORMATS.map(renderFormat)} </Form> ) ``` ## Custom Rule Verification #### Markup Schema Cases ```tsx import React from 'react' import { createForm, registerValidateRules } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, NumberPicker, }, }) registerValidateRules({ global_1(value) { if (!value) return '' return value !== '123' ? 'error❎' : '' }, global_2(value, rule) { if (!value) return '' return value !== '123' ? rule.message : '' }, global_3(value) { if (!value) return '' return value === '123' }, global_4(value) { if (!value) return '' if (value < 10) { return { type: 'error', message: 'The value cannot be less than 10', } } else if (value < 100) { return { type: 'warning', message: 'The value is within 100', } } else if (value < 1000) { return { type: 'success', message: 'The value is greater than 100 and less than 1000', } } }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="global_style_1" title="Global registration style" required x-validator={{ global_1: true, }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="global_style_2" title="Global registration style" required x-validator={{ global_2: true, message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="global_style_3" title="Global registration style" required x-validator={{ global_3: true, message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.Number name="global_style_4" title="Global registration style" required x-validator={{ global_4: true, }} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.String name="validator_style_1" title="Locally defined style" required x-validator={(value) => { if (!value) return '' return value !== '123' ? 'error❎' : '' }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="validator_style_2" title="Locally defined style" required x-validator={{ validator(value, rule) { if (!value) return '' return value !== '123' ? rule.message : '' }, message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="validator_style_3" title="Locally defined style" required x-validator={{ validator(value) { if (!value) return '' return value === '123' }, message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.Number name="validator_style_4" title="Locally defined style" required x-validator={(value) => { if (!value) return '' if (value < 10) { return { type: 'error', message: 'The value cannot be less than 10', } } else if (value < 100) { return { type: 'warning', message: 'The value is within 100', } } else if (value < 1000) { return { type: 'success', message: 'The value is greater than 100 and less than 1000', } } }} x-component="NumberPicker" x-decorator="FormItem" /> </SchemaField> </Form> ) ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm, registerValidateRules } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, NumberPicker, }, }) registerValidateRules({ global_1(value) { if (!value) return '' return value !== '123' ? 'error❎' : '' }, global_2(value, rule) { if (!value) return '' return value !== '123' ? rule.message : '' }, global_3(value) { if (!value) return '' return value === '123' }, global_4(value) { if (!value) return '' if (value < 10) { return { type: 'error', message: 'The value cannot be less than 10', } } else if (value < 100) { return { type: 'warning', message: 'The value is within 100', } } else if (value < 1000) { return { type: 'success', message: 'The value is greater than 100 and less than 1000', } } }, }) const schema = { type: 'object', properties: { global_style_1: { title: 'Global registration style', required: true, 'x-validator': { global_1: true, }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_2: { title: 'Global registration style', required: true, 'x-validator': { global_2: true, message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_3: { title: 'Global registration style', required: true, 'x-validator': { global_3: true, message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_4: { title: 'Global registration style', required: true, 'x-validator': { global_4: true, }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_1: { title: 'Locally defined style', required: true, 'x-validator': `{{(value)=> { if (!value) return '' return value !== '123' ? 'error❎' : '' }}}`, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_2: { title: 'Locally defined style', required: true, 'x-validator': { validator: `{{(value, rule)=> { if (!value) return '' return value !== '123' ? rule.message : '' }}}`, message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_3: { title: 'Locally defined style', required: true, 'x-validator': { validator: `{{(value, rule)=> { if (!value) return '' return value === '123' }}}`, message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_4: { title: 'Locally defined style', required: true, 'x-validator': `{{(value, rule)=> { if (!value) return '' if (value < 10) { return { type: 'error', message: 'The value cannot be less than 10', } } else if (value < 100) { return { type: 'warning', message: 'The value is within 100', } } else if (value < 1000) { return { type: 'success', message: 'The value is greater than 100 and less than 1000', } } }}}`, 'x-component': 'Input', 'x-decorator': 'FormItem', }, }, } export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm, registerValidateRules } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' const form = createForm() registerValidateRules({ global_1(value) { if (!value) return '' return value !== '123' ? 'error❎' : '' }, global_2(value, rule) { if (!value) return '' return value !== '123' ? rule.message : '' }, global_3(value) { if (!value) return '' return value === '123' }, global_4(value) { if (!value) return '' if (value < 10) { return { type: 'error', message: 'The value cannot be less than 10', } } else if (value < 100) { return { type: 'warning', message: 'The value is within 100', } } else if (value < 1000) { return { type: 'success', message: 'The value is greater than 100 and less than 1000', } } }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <Field name="global_style_1" title="Global registration style" required validator={{ global_1: true, }} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_2" title="Global registration style" required validator={{ global_2: true, message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_3" title="Global registration style" required validator={{ global_3: true, message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_4" title="Global registration style" required validator={{ global_4: true, }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="validator_style_1" title="Locally defined style" required validator={(value) => { if (!value) return '' return value !== '123' ? 'error❎' : '' }} component={[Input]} decorator={[FormItem]} /> <Field name="validator_style_2" title="Locally defined style" required validator={{ validator(value, rule) { if (!value) return '' return value !== '123' ? rule.message : '' }, message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="validator_style_3" title="Locally defined style" required validator={{ validator(value) { if (!value) return '' return value === '123' }, message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="validator_style_4" title="Locally defined style" required validator={(value) => { if (!value) return '' if (value < 10) { return { type: 'error', message: 'The value cannot be less than 10', } } else if (value < 100) { return { type: 'warning', message: 'The value is within 100', } } else if (value < 1000) { return { type: 'success', message: 'The value is greater than 100 and less than 1000', } } }} component={[NumberPicker]} decorator={[FormItem]} /> </Form> ) ``` ## Using Third-Party Validation Libraries With the powerful validation engine of Formily, it is extremely convenient to adapt to third-party validation libraries such as yup. Here is an example of how to use it: #### JSON Schema Cases ```tsx import React from 'react' import { createForm, registerValidateRules } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' import { string } from 'yup' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, NumberPicker, }, }) registerValidateRules({ yup: async (value, rule) => { try { await rule.yup().validate(value) return '' // Return an empty string when validation is successful } catch (err) { return err.errors.join(',') // Return the error message when validation fails } }, }) const schema = { type: 'object', properties: { global_style_1: { title: 'Maximum length is 2', 'x-validator': [ { triggerType: 'onBlur', yup: () => string().required('required'), }, { triggerType: 'onBlur', yup: () => string().max(2, 'Maximum length is 2'), }, ], 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_2: { title: 'email', required: true, 'x-validator': { triggerType: 'onBlur', yup: () => string().email(), }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, }, } export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm, registerValidateRules } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input, NumberPicker } from '@formily/antd' import { string, number } from 'yup' const form = createForm() registerValidateRules({ yup: async (value, rule) => { try { await rule.yup().validate(value) return '' // Return an empty string when validation is successful } catch (err) { return err.errors.join(',') // Return the error message when validation fails } }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <Field name="global_style_1" title="email" required validator={{ yup: () => string().email(), }} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_2" title="max 30" required validator={{ yup: () => number().max(30), }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="global_style_3" title="email" required validator={{ yup: () => string().email(), }} component={[Input]} decorator={[FormItem]} /> </Form> ) ``` ## Custom Format Verification #### Markup Schema Cases ```tsx import React from 'react' import { createForm, registerValidateFormats } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) registerValidateFormats({ custom_format: /123/, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="global_style_1" title="Global registration style" required x-validator={{ format: 'custom_format', message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="global_style_2" title="Global registration style" required x-validator={'custom_format'} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="global_style_3" title="Global registration style" required x-validator={['custom_format']} x-component="Input" x-decorator="FormItem" /> <SchemaField.Number name="global_style_4" title="Global registration style" required x-validator={{ format: 'custom_format', message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="validator_style_1" title="Locally defined style" required pattern={/123/} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="validator_style_2" title="Locally defined style" required pattern="123" x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="validator_style_3" title="Locally defined style" required x-validator={{ pattern: /123/, message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="validator_style_4" title="Locally defined style" required x-validator={{ pattern: '123', message: 'error❎', }} x-component="Input" x-decorator="FormItem" /> </SchemaField> </Form> ) ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm, registerValidateFormats } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) registerValidateFormats({ custom_format: /123/, }) const schema = { type: 'object', properties: { global_style_1: { title: 'Global registration style', required: true, 'x-validator': { format: 'custom_format', message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_2: { title: 'Global registration style', required: true, 'x-validator': 'custom_format', 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_3: { title: 'Global registration style', required: true, 'x-validator': ['custom_format'], 'x-component': 'Input', 'x-decorator': 'FormItem', }, global_style_4: { title: 'Global registration style', required: true, 'x-validator': { format: 'custom_format', message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_1: { title: 'Locally defined style', required: true, pattern: /123/, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_2: { title: 'Locally defined style', required: true, pattern: '123', 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_3: { title: 'Locally defined style', required: true, 'x-validator': { pattern: /123/, message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, validator_style_4: { title: 'Locally defined style', required: true, 'x-validator': { pattern: '123', message: 'error❎', }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, }, } export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm, registerValidateFormats } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() registerValidateFormats({ custom_format: /123/, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <Field name="global_style_1" title="Global registration style" required validator={{ format: 'custom_format', message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_2" title="Global registration style" required validator={'custom_format'} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_3" title="Global registration style" required validator={['custom_format']} component={[Input]} decorator={[FormItem]} /> <Field name="global_style_4" title="Global registration style" required validator={{ format: 'custom_format', message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="validator_style_1" title="Locally defined style" required validator={{ pattern: /123/, message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> <Field name="validator_style_2" title="Locally defined style" required validator={{ pattern: '123', message: 'error❎', }} component={[Input]} decorator={[FormItem]} /> </Form> ) ``` ## Asynchronous Verification #### Markup Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="async_validate" title="Asynchronous verification" required x-validator={(value) => { return new Promise((resolve) => { setTimeout(() => { if (!value) { resolve('') } if (value === '123') { resolve('') } else { resolve('error❎') } }, 1000) }) }} x-component="Input" x-decorator="FormItem" /> <SchemaField.String name="async_validate_2" title="Asynchronous verification (onBlur trigger)" required x-validator={{ triggerType: 'onBlur', validator: (value) => { return new Promise((resolve) => { setTimeout(() => { if (!value) { resolve('') } if (value === '123') { resolve('') } else { resolve('error❎') } }, 1000) }) }, }} x-component="Input" x-decorator="FormItem" /> </SchemaField> </Form> ) ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) const schema = { type: 'object', properties: { async_validate: { title: 'Asynchronous verification', required: true, 'x-validator': `{{(value) => { return new Promise((resolve) => { setTimeout(() => { if (!value) { resolve('') } if (value === '123') { resolve('') } else { resolve('error❎') } }, 1000) }) }}}`, 'x-component': 'Input', 'x-decorator': 'FormItem', }, async_validate_2: { title: 'Asynchronous verification (onBlur trigger)', required: true, 'x-validator': { triggerType: 'onBlur', validator: `{{(value) => { return new Promise((resolve) => { setTimeout(() => { if (!value) { resolve('') } if (value === '123') { resolve('') } else { resolve('错误❎') } }, 1000) }) }}}`, }, 'x-component': 'Input', 'x-decorator': 'FormItem', }, }, } export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <Field name="async_validate" title="Asynchronous verification" required validator={(value) => { return new Promise((resolve) => { setTimeout(() => { if (!value) { resolve('') } if (value === '123') { resolve('') } else { resolve('error❎') } }, 1000) }) }} component={[Input]} decorator={[FormItem]} /> <Field name="async_validate_2" title="Asynchronous verification (onBlur trigger)" required validator={{ triggerType: 'onBlur', validator: (value) => { return new Promise((resolve) => { setTimeout(() => { if (!value) { resolve('') } if (value === '123') { resolve('') } else { resolve('error ❎') } }, 1000) }) }, }} component={[Input]} decorator={[FormItem]} /> </Form> ) ``` ## Linkage Verification #### Markup Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, NumberPicker } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="aa" title="AA" required x-reactions={(field) => { field.selfErrors = field.query('bb').value() >= field.value ? 'AA must be greater than BB' : '' }} x-component="NumberPicker" x-decorator="FormItem" /> <SchemaField.String name="bb" title="BB" required x-reactions={(field) => { field.selfErrors = field.query('aa').value() <= field.value ? 'AA must be greater than BB' : '' }} x-component="NumberPicker" x-decorator="FormItem" /> </SchemaField> </Form> ) ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, NumberPicker } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const schema = { type: 'object', properties: { aa: { title: 'AA', required: true, 'x-reactions': `{{(field) => { field.selfErrors = field.query('bb').value() >= field.value ? 'AA must be greater than BB' : '' }}}`, 'x-component': 'NumberPicker', 'x-decorator': 'FormItem', }, bb: { title: 'BB', required: true, 'x-reactions': { dependencies: ['aa'], fulfill: { state: { selfErrors: "{{$deps[0] <= $self.value ? 'AA must be greater than BB' : ''}}", }, }, }, 'x-component': 'NumberPicker', 'x-decorator': 'FormItem', }, }, } export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> </Form> ) ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, NumberPicker } from '@formily/antd' const form = createForm() export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <Field name="aa" title="AA" required reactions={(field) => { field.selfErrors = field.query('bb').value() >= field.value ? 'AA must be greater than BB' : '' }} component={[NumberPicker]} decorator={[FormItem]} /> <Field name="bb" title="BB" required reactions={(field) => { field.selfErrors = field.query('aa').value() <= field.value ? 'AA must be greater than BB' : '' }} component={[NumberPicker]} decorator={[FormItem]} /> </Form> ) ``` ## Custom Verification Messages Mainly through [registerValidateLocale](https://core.formilyjs.org/api/entry/form-validator-registry#registervalidatelocale) to customize the built-in verification messages ```tsx import React from 'react' import { createForm, registerValidateLocale, setValidateLanguage, } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input } from '@formily/antd' const form = createForm() const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) setValidateLanguage('en-US') registerValidateLocale({ 'en-US': { required: 'Custom required verification message', }, }) export default () => ( <Form form={form} labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="aa" title="AA" required x-component="Input" x-decorator="FormItem" /> </SchemaField> </Form> ) ``` ``` -------------------------------------------------------------------------------- /docs/guide/scenes/edit-detail.md: -------------------------------------------------------------------------------- ```markdown # Edit Details ## Edit #### Markup Schema Cases ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, Submit, FormGrid, Upload, ArrayItems, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>Upload a copy</Button> </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: 'Zhang San', phone: '13245633378', email: '[email protected]', }, { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="Edit User" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="Username" required x-decorator="FormItem" x-component="Input" /> <SchemaField.Void title="Name" x-decorator="FormItem" x-decorator-props={{ asterisk: true, feedbackLayout: 'none', }} x-component="FormGrid" > <SchemaField.String name="firstName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: 'firstName', }} required /> <SchemaField.String name="lastName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: 'lastname', }} required /> </SchemaField.Void> <SchemaField.String name="email" title="Email" required x-validator="email" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="gender" title="Gender" x-decorator="FormItem" x-component="Select" enum={[ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ]} required /> <SchemaField.String name="birthday" title="Birthday" required x-decorator="FormItem" x-component="DatePicker" /> <SchemaField.String name="address" title="Address" required x-decorator="FormItem" x-component="Cascader" x-reactions="{{fetchAddress}}" /> <SchemaField.String name="idCard" title="ID" required x-decorator="FormItem" x-component="IDUpload" /> <SchemaField.Array name="contacts" title="Contacts" required x-decorator="FormItem" x-component="ArrayItems" > <SchemaField.Object x-component="ArrayItems.Item"> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.SortHandle" /> <SchemaField.Void name="popover" title="Contact Informations" x-decorator="Editable.Popover" x-component="FormLayout" x-component-props={{ layout: 'vertical', }} x-reactions={[ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ]} > <SchemaField.String name="name" required title="Name" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="email" title="Email" x-validator={[{ required: true }, 'email']} x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="phone" required title="Phone Number" x-validator="phone" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> </SchemaField.Void> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.Remove" /> </SchemaField.Object> <SchemaField.Void x-component="ArrayItems.Addition" title="Add Contact" /> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit block size="large"> Submit </Submit> </FormButtonGroup.FormItem> </Form> </Spin> </Card> </div> ) } ``` #### JSON Schema Cases ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, Submit, FormGrid, Upload, ArrayItems, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>Upload a copy</Button> </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) const schema = { type: 'object', properties: { username: { type: 'string', title: 'Username', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, name: { type: 'void', title: 'Name', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'FormGrid', properties: { firstName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: 'firstName', }, }, lastName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: 'lastname', }, }, }, }, email: { type: 'string', title: 'Email', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', }, gender: { type: 'string', title: 'Gender', enum: [ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', }, birthday: { type: 'string', required: true, title: 'Birthday', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, address: { type: 'string', required: true, title: 'Address', 'x-decorator': 'FormItem', 'x-component': 'Cascader', 'x-reactions': '{{fetchAddress}}', }, idCard: { type: 'string', required: true, title: 'ID', 'x-decorator': 'FormItem', 'x-component': 'IDUpload', }, contacts: { type: 'array', required: true, title: 'Contacts', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems', items: { type: 'object', 'x-component': 'ArrayItems.Item', properties: { sort: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.SortHandle', }, popover: { type: 'void', title: 'Contact Informations', 'x-decorator': 'Editable.Popover', 'x-component': 'FormLayout', 'x-component-props': { layout: 'vertical', }, 'x-reactions': [ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ], properties: { name: { type: 'string', title: 'Name', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { style: { width: 300, }, }, }, email: { type: 'string', title: 'Email', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'email'], 'x-component-props': { style: { width: 300, }, }, }, phone: { type: 'string', title: 'Phone Number', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'phone'], 'x-component-props': { style: { width: 300, }, }, }, }, }, remove: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.Remove', }, }, }, properties: { addition: { type: 'void', title: 'Add Contact', 'x-component': 'ArrayItems.Addition', }, }, }, }, } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: 'Zhang San', phone: '13245633378', email: '[email protected]', }, { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="Edit User" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField schema={schema} /> <FormButtonGroup.FormItem> <Submit block size="large"> Submit </Submit> </FormButtonGroup.FormItem> </Form> </Spin> </Card> </div> ) } ``` #### Pure JSX Cases ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { Field, VoidField, ArrayField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, Submit, FormGrid, Upload, ArrayBase, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' import './index.less' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>Upload a copy</Button> </Upload> ) } const fetchAddress = (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: 'Zhang San', phone: '13245633378', email: '[email protected]', }, { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="Edit User" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <Field name="username" title="Username" required decorator={[FormItem]} component={[Input]} /> <VoidField name="name" title="Name" decorator={[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]} component={[FormGrid]} > <Field name="firstName" decorator={[FormItem]} component={[ Input, { placeholder: 'firstName', }, ]} required /> <Field name="lastName" decorator={[FormItem]} component={[ Input, { placeholder: 'lastname', }, ]} required /> </VoidField> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[Input]} /> <Field name="gender" title="Gender" decorator={[FormItem]} component={[Select]} dataSource={[ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ]} required /> <Field name="birthday" title="Birthday" required decorator={[FormItem]} component={[DatePicker]} /> <Field name="address" title="Address" required decorator={[FormItem]} component={[Cascader]} reactions={fetchAddress} /> <Field name="idCard" title="ID" required decorator={[FormItem]} component={[IDUpload]} /> <ArrayField name="contacts" title="Contacts" decorator={[FormItem]}> {(field) => ( <ArrayBase> {field.value?.map((item, index) => ( <div key={index} className="array-items-item"> <Field name={`${index}`} title="Contact Informations" component={[Editable.Popover]} reactions={(field) => { field.title = field.query('.[].name').value() || field.title }} > <VoidField name="layout" component={[FormLayout, { layout: 'vertical' }]} > <Field name="name" title="Name" required decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="phone" title="Phone Number" required validator="phone" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> </VoidField> </Field> <FormItem.BaseItem> <ArrayBase.Remove index={index} /> <ArrayBase.MoveDown index={index} /> <ArrayBase.MoveUp index={index} /> </FormItem.BaseItem> </div> ))} <ArrayBase.Addition title="Add Contact" /> </ArrayBase> )} </ArrayField> <FormButtonGroup.FormItem> <Submit block size="large"> Submit </Submit> </FormButtonGroup.FormItem> </Form> </Spin> </Card> </div> ) } ``` ## Details #### Markup Schema Cases ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField, useField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, FormGrid, Upload, ArrayItems, Editable, PreviewText, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ readPretty: true, validateFirst: true, }) const IDUpload = (props) => { const field = useField() return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > {field.editable && ( <Button icon={<UploadOutlined />}>Upload a copy</Button> )} </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: 'Zhang San', phone: '13245633378', email: '[email protected]', }, { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <PreviewText.Placeholder value="-"> <Card title="User Details" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="Username" required x-decorator="FormItem" x-component="Input" /> <SchemaField.Void title="Name" x-decorator="FormItem" x-decorator-props={{ feedbackLayout: 'none', }} x-component="FormGrid" > <SchemaField.String name="firstName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: 'firstName', }} required /> <SchemaField.String name="lastName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: 'lastname', }} required /> </SchemaField.Void> <SchemaField.String name="email" title="Email" required x-validator="email" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="gender" title="Gender" x-decorator="FormItem" x-component="Select" enum={[ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ]} required /> <SchemaField.String name="birthday" title="Birthday" required x-decorator="FormItem" x-component="DatePicker" /> <SchemaField.String name="address" title="Address" required x-decorator="FormItem" x-component="Cascader" x-reactions="{{fetchAddress}}" /> <SchemaField.String name="idCard" title="ID" required x-decorator="FormItem" x-component="IDUpload" /> <SchemaField.Array name="contacts" title="Contacts" required x-decorator="FormItem" x-component="ArrayItems" > <SchemaField.Object x-component="ArrayItems.Item"> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.SortHandle" /> <SchemaField.Void name="popover" title="Contact Informations" x-decorator="Editable.Popover" x-component="FormLayout" x-component-props={{ layout: 'vertical', }} x-reactions={[ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ]} > <SchemaField.String name="name" required title="Name" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="email" title="Email" x-validator={[{ required: true }, 'email']} x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="phone" required title="Phone Number" x-validator="phone" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> </SchemaField.Void> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.Remove" /> </SchemaField.Object> <SchemaField.Void x-component="ArrayItems.Addition" title="Add Contact" /> </SchemaField.Array> </SchemaField> </Form> </Spin> </Card> </PreviewText.Placeholder> </div> ) } ``` #### JSON Schema Cases ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField, useField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, FormGrid, Upload, ArrayItems, Editable, PreviewText, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ readPretty: true, validateFirst: true, }) const IDUpload = (props) => { const field = useField() return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > {field.editable && ( <Button icon={<UploadOutlined />}>Upload a copy</Button> )} </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) const schema = { type: 'object', properties: { username: { type: 'string', title: 'Username', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, name: { type: 'void', title: 'Name', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'FormGrid', properties: { firstName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: 'firstName', }, }, lastName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: 'lastname', }, }, }, }, email: { type: 'string', title: 'Email', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', }, gender: { type: 'string', title: 'Gender', enum: [ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', }, birthday: { type: 'string', required: true, title: 'Birthday', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, address: { type: 'string', required: true, title: 'Address', 'x-decorator': 'FormItem', 'x-component': 'Cascader', 'x-reactions': '{{fetchAddress}}', }, idCard: { type: 'string', required: true, title: 'ID', 'x-decorator': 'FormItem', 'x-component': 'IDUpload', }, contacts: { type: 'array', required: true, title: 'Contacts', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems', items: { type: 'object', 'x-component': 'ArrayItems.Item', properties: { sort: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.SortHandle', }, popover: { type: 'void', title: 'Contact Informations', 'x-decorator': 'Editable.Popover', 'x-component': 'FormLayout', 'x-component-props': { layout: 'vertical', }, 'x-reactions': [ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ], properties: { name: { type: 'string', title: 'Name', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { style: { width: 300, }, }, }, email: { type: 'string', title: 'Email', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'email'], 'x-component-props': { style: { width: 300, }, }, }, phone: { type: 'string', title: 'Phone Number', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'phone'], 'x-component-props': { style: { width: 300, }, }, }, }, }, remove: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.Remove', }, }, }, properties: { addition: { type: 'void', title: 'Add Contact', 'x-component': 'ArrayItems.Addition', }, }, }, }, } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: 'Zhang San', phone: '13245633378', email: '[email protected]', }, { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <PreviewText.Placeholder value="-"> <Card title="User Details" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField schema={schema} /> </Form> </Spin> </Card> </PreviewText.Placeholder> </div> ) } ``` #### Pure JSX Cases ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { Field, VoidField, ArrayField, useField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, FormGrid, ArrayBase, Upload, PreviewText, Editable, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' import './index.less' const form = createForm({ validateFirst: true, readPretty: true, }) const IDUpload = (props) => { const field = useField() return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > {field.editable && ( <Button icon={<UploadOutlined />}>Upload a copy</Button> )} </Upload> ) } const fetchAddress = (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: 'Zhang San', phone: '13245633378', email: '[email protected]', }, { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <PreviewText.Placeholder value="-"> <Card title="Edit User" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <Field name="username" title="Username" required decorator={[FormItem]} component={[Input]} /> <VoidField name="name" title="Name" decorator={[ FormItem, { feedbackLayout: 'none', }, ]} component={[FormGrid]} > <Field name="firstName" decorator={[FormItem]} component={[ Input, { placeholder: 'firstName', }, ]} required /> <Field name="lastName" decorator={[FormItem]} component={[ Input, { placeholder: 'lastname', }, ]} required /> </VoidField> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[Input]} /> <Field name="gender" title="Gender" decorator={[FormItem]} component={[Select]} dataSource={[ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ]} required /> <Field name="birthday" title="Birthday" required decorator={[FormItem]} component={[DatePicker]} /> <Field name="address" title="Address" required decorator={[FormItem]} component={[Cascader]} reactions={fetchAddress} /> <Field name="idCard" title="ID" required decorator={[FormItem]} component={[IDUpload]} /> <ArrayField name="contacts" title="Contacts" decorator={[FormItem]} > {(field) => ( <ArrayBase> {field.value?.map((item, index) => ( <div key={index} className="array-items-item"> <Field name={`${index}`} title="Contact Informations" component={[Editable.Popover]} reactions={(field) => { field.title = field.query('.[].name').value() || field.title }} > <VoidField name="layout" component={[FormLayout, { layout: 'vertical' }]} > <Field name="name" title="Name" required decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="phone" title="Phone Number" required validator="phone" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> </VoidField> </Field> </div> ))} <ArrayBase.Addition title="Add Contact" /> </ArrayBase> )} </ArrayField> </Form> </Spin> </Card> </PreviewText.Placeholder> </div> ) } ``` ```