diff --git a/components/form/Form.jsx b/components/form/Form.jsx index 63de4a8e3..b29b0c743 100755 --- a/components/form/Form.jsx +++ b/components/form/Form.jsx @@ -57,6 +57,8 @@ export const FormProps = { // onSubmit: React.FormEventHandler; prefixCls: PropTypes.string, hideRequiredMark: PropTypes.bool, + autoFormCreate: PropTypes.func, + options: PropTypes.object, } export const ValidationRule = { @@ -143,23 +145,6 @@ export default { fieldDataProp: FIELD_DATA_PROP, }) }, - - // constructor (props) { - // super(props) - - // warning(!props.form, 'It is unnecessary to pass `form` to `Form` after antd@1.7.0.') - // } - - // shouldComponentUpdate(...args) { - // return PureRenderMixin.shouldComponentUpdate.apply(this, args); - // } - - // getChildContext () { - // const { layout } = this.props - // return { - // vertical: layout === 'vertical', - // } - // }, provide () { return { FormProps: this.$props, @@ -178,7 +163,7 @@ export default { render () { const { - prefixCls, hideRequiredMark, layout, onSubmit, $slots, + prefixCls, hideRequiredMark, layout, onSubmit, $slots, autoFormCreate, options = {}, } = this const formClassName = classNames(prefixCls, { @@ -187,6 +172,43 @@ export default { [`${prefixCls}-inline`]: layout === 'inline', [`${prefixCls}-hide-required-mark`]: hideRequiredMark, }) + if (autoFormCreate) { + const DomForm = this.DomForm || createDOMForm({ + fieldNameProp: 'id', + ...options, + fieldMetaProp: FIELD_META_PROP, + fieldDataProp: FIELD_DATA_PROP, + templateContext: this.$parent, + })({ + provide () { + return { + decoratorFormProps: this.$props, + } + }, + data () { + return { + children: $slots.default, + formClassName: formClassName, + submit: onSubmit, + } + }, + created () { + autoFormCreate(this.form) + }, + render () { + const { children, formClassName, submit } = this + return
{children}
+ }, + }) + if (this.domForm) { + this.domForm.children = $slots.default + this.domForm.submit = onSubmit + this.domForm.formClassName = formClassName + } + this.DomForm = DomForm + + return { this.domForm = inst }}/> + } return
{$slots.default}
}, diff --git a/components/form/FormItem.jsx b/components/form/FormItem.jsx index cff6fa92f..552ac2fd2 100644 --- a/components/form/FormItem.jsx +++ b/components/form/FormItem.jsx @@ -20,6 +20,8 @@ export const FormItemProps = { hasFeedback: PropTypes.bool, required: PropTypes.bool, colon: PropTypes.bool, + fieldDecoratorId: PropTypes.string, + fieldDecoratorOptions: PropTypes.object, } export default { @@ -33,21 +35,18 @@ export default { }), inject: { FormProps: { default: {}}, + decoratorFormProps: { default: {}}, }, data () { return { helpShow: false } }, mounted () { warning( - this.getControls(this.$slots.default, true).length <= 1, + this.getControls(this.slotDefault, true).length <= 1, '`Form.Item` cannot generate `validateStatus` and `help` automatically, ' + 'while there are more than one `getFieldDecorator` in it.', ) }, - - // shouldComponentUpdate(...args: any[]) { - // return PureRenderMixin.shouldComponentUpdate.apply(this, args); - // } methods: { getHelpMsg () { const help = getComponentFromProp(this, 'help') @@ -90,7 +89,7 @@ export default { }, getOnlyControl () { - const child = this.getControls(this.$slots.default, false)[0] + const child = this.getControls(this.slotDefault, false)[0] return child !== undefined ? child : null }, @@ -303,12 +302,11 @@ export default { ) : null }, renderChildren () { - const { $slots } = this return [ this.renderLabel(), this.renderWrapper( this.renderValidateWrapper( - filterEmpty($slots.default || []), + this.slotDefault, this.renderHelp(), this.renderExtra(), ), @@ -333,6 +331,18 @@ export default { }, render () { + const { $slots, decoratorFormProps, fieldDecoratorId, fieldDecoratorOptions = {}} = this + const child = filterEmpty($slots.default || []) + if (decoratorFormProps.form && fieldDecoratorId && child.length) { + const getFieldDecorator = decoratorFormProps.form.getFieldDecorator + child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions)(child[0]) + warning( + !(child.length > 1), + '`autoFormCreate` just `decorator` then first children. but you can use JSX to support multiple children', + ) + } + + this.slotDefault = child const children = this.renderChildren() return this.renderFormItem(children) }, diff --git a/components/form/__tests__/__snapshots__/demo.test.js.snap b/components/form/__tests__/__snapshots__/demo.test.js.snap index 136d0a5a0..8b9116297 100644 --- a/components/form/__tests__/__snapshots__/demo.test.js.snap +++ b/components/form/__tests__/__snapshots__/demo.test.js.snap @@ -221,7 +221,7 @@ exports[`renders ./components/form/demo/dynamic-form-item.vue correctly 1`] = ` `; exports[`renders ./components/form/demo/dynamic-rule.vue correctly 1`] = ` -
+
@@ -244,7 +244,9 @@ exports[`renders ./components/form/demo/dynamic-rule.vue correctly 1`] = `
-
+
@@ -258,7 +260,7 @@ exports[`renders ./components/form/demo/dynamic-rule.vue correctly 1`] = `
-
+
`; exports[`renders ./components/form/demo/form-in-modal.vue correctly 1`] = ` @@ -314,7 +316,7 @@ exports[`renders ./components/form/demo/horizontal-login.vue correctly 1`] = `
-
+
diff --git a/components/form/demo/advanced-search.vue b/components/form/demo/advanced-search.vue index 832a7c917..1db20195b 100644 --- a/components/form/demo/advanced-search.vue +++ b/components/form/demo/advanced-search.vue @@ -13,6 +13,7 @@ Because the width of label is not fixed, you may need to adjust it by customizin + diff --git a/components/form/demo/dynamic-rule.vue b/components/form/demo/dynamic-rule.vue index fccac58ca..6a17d2372 100644 --- a/components/form/demo/dynamic-rule.vue +++ b/components/form/demo/dynamic-rule.vue @@ -9,9 +9,47 @@ Perform different check rules according to different situations. - + diff --git a/components/form/demo/horizontal-login.vue b/components/form/demo/horizontal-login.vue index 75c35a433..04998b98c 100644 --- a/components/form/demo/horizontal-login.vue +++ b/components/form/demo/horizontal-login.vue @@ -9,19 +9,70 @@ Horizontal login form is often used in navigation bar. - + diff --git a/components/form/demo/index.vue b/components/form/demo/index.vue index e12c06e7b..168aefc0e 100644 --- a/components/form/demo/index.vue +++ b/components/form/demo/index.vue @@ -85,33 +85,40 @@ export default { return (
- - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - @@ -121,12 +128,6 @@ export default { - - - - - - diff --git a/components/form/index.en-US.md b/components/form/index.en-US.md index 29cc1e9f8..f220e4912 100644 --- a/components/form/index.en-US.md +++ b/components/form/index.en-US.md @@ -14,12 +14,31 @@ | form | Decorated by `Form.create()` will be automatically set `this.form` property, so just pass to form, you don't need to set it by yourself after 1.7.0. | object | n/a | | hideRequiredMark | Hide required mark of all form items | Boolean | false | | layout | Define form layout(Support after 2.8) | 'horizontal'\|'vertical'\|'inline' | 'horizontal' | +| autoFormCreate | Automate Form.create, Recommended for use under the `template` component, and cannot be used with `Form.create()` |Function(form)| | +| options | The `options` corresponding to `Form.create(options)` | Object | {} | ### Events | Events Name | Description | Arguments | | --- | --- | --- | | submit | Defines a function will be called if form data validation is successful. | Function(e:Event) | +### autoFormCreate + +````html + +... + +```` +If you use the `template` syntax, you can use ʻautoFormCreate` to turn on automatic validation and data collection, but each `Form.Item` only to `decorator` for its first child. More complex features suggest `JSX`. + +Related examples are as follows: + +[coordinated-controls](/ant-design/components/form/#components-form-demo-coordinated-controls) + +[dynamic-rules](/ant-design/components/form/#components-form-demo-dynamic-rules) + +[horizontal-login-form](/ant-design/components/form/#components-form-demo-horizontal-login-form) + ### Form.create(options) How to use: @@ -124,6 +143,8 @@ Note: | required | Whether provided or not, it will be generated by the validation rule. | boolean | false | | validateStatus | The validation status. If not provided, it will be generated by validation rule. options: 'success' 'warning' 'error' 'validating' | string | | | wrapperCol | The layout for input controls, same as `labelCol` | [object](/ant-design/components/grid/#Col) | | +| fieldDecoratorId | Corresponds to the first parameter `id` of `getFieldDecorator(id, options)`. If you need to collect data, you need to set this field. | string | | +| fieldDecoratorOptions | Corresponds to the second parameter `options` of `getFieldDecorator(id, options)`. | object | {} | ### Validation Rules diff --git a/components/form/index.zh-CN.md b/components/form/index.zh-CN.md index 0f3e424c3..9b05e17b8 100644 --- a/components/form/index.zh-CN.md +++ b/components/form/index.zh-CN.md @@ -14,12 +14,31 @@ | form | 经 `Form.create()` 包装过的组件会自带 `this.form` 属性,直接传给 Form 即可。无需手动设置 | object | 无 | | hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false | | layout | 表单布局 | 'horizontal'\|'vertical'\|'inline' | 'horizontal' | +| autoFormCreate | 自动执行Form.create,建议在template组件下使用,并且不可以和`Form.create()`同时使用 |Function(form)| 无| +| options | 对应Form.create(options)的`options` | Object | {} | ### 事件 | 事件名称 | 说明 | 回调参数 | | --- | --- | --- | | submit | 数据验证成功后回调事件 | Function(e:Event) | +### autoFormCreate + +````html + +... + +```` +如果使用`template`语法,可以使用`autoFormCreate`开启自动校验和数据收集功能,但每一个`Form.Item`仅仅对其第一个子组件进行`decorator`。更加复杂的功能建议使用`JSX`。 + +相关示例如下: + +[coordinated-controls](/ant-design/components/form-cn/#components-form-demo-coordinated-controls) + +[dynamic-rules](/ant-design/components/form-cn/#components-form-demo-dynamic-rules) + +[horizontal-login-form](/ant-design/components/form-cn/#components-form-demo-horizontal-login-form) + ### Form.create(options) 使用方式如下: @@ -123,6 +142,8 @@ CustomizedForm = Form.create({})(CustomizedForm); | required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false | | validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | | | wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/ant-design/components/grid-cn/#Col) | | +| fieldDecoratorId | 对应`getFieldDecorator(id, options)`的第一个参数`id`,如需收集数据需要设置该字段 | string | 无 | +| fieldDecoratorOptions | 对应`getFieldDecorator(id, options)`的第二个参数`options` | object | {} | ### 校验规则 diff --git a/components/vc-form/src/createBaseForm.jsx b/components/vc-form/src/createBaseForm.jsx index 151f5c750..79dcf5c05 100644 --- a/components/vc-form/src/createBaseForm.jsx +++ b/components/vc-form/src/createBaseForm.jsx @@ -35,6 +35,7 @@ function createBaseForm (option = {}, mixins = []) { fieldDataProp, formPropName = 'form', props = {}, + templateContext, } = option return function decorate (WrappedComponent) { @@ -177,7 +178,7 @@ function createBaseForm (option = {}, mixins = []) { warning( !(valuePropName in originalProps), `\`getFieldDecorator\` will override \`${valuePropName}\`, ` + - `so please don't set \`${valuePropName}\` directly ` + + `so please don't set \`${valuePropName} and v-model\` directly ` + `and use \`setFieldsValue\` to set it.` ) const defaultValuePropName = @@ -327,7 +328,11 @@ function createBaseForm (option = {}, mixins = []) { .reduce((acc, name) => set(acc, name, this.fieldsStore.getField(name)), {}) onFieldsChange(this, changedFields, this.fieldsStore.getNestedAllFields()) } - this.$forceUpdate() + if (templateContext) { + templateContext.$forceUpdate() + } else { + this.$forceUpdate() + } this.$nextTick(() => { callback && callback() }) diff --git a/site/dev.js b/site/dev.js index 56cfa6a0b..3a52133ed 100644 --- a/site/dev.js +++ b/site/dev.js @@ -10,7 +10,7 @@ import Api from './components/api' import './components' import demoBox from './components/demoBox' import demoContainer from './components/demoContainer' -import Test from '../components/list/demo/test' +import Test from '../components/form/demo/index' Vue.use(VueClipboard) Vue.use(VueRouter)