From c80e54ba785eabd933ae5c8a5b86e333bbcf7da3 Mon Sep 17 00:00:00 2001 From: tanjinzhou <415800467@qq.com> Date: Fri, 30 Aug 2019 14:07:32 +0800 Subject: [PATCH] feat: form add selfUpdate #1049 --- components/form/Form.jsx | 1 + components/form/FormItem.jsx | 10 ++++++-- components/form/index.zh-CN.md | 8 ++++++ components/vc-form/src/createBaseForm.jsx | 30 +++++++++++++++-------- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/components/form/Form.jsx b/components/form/Form.jsx index 6be024521..1a5f23d23 100755 --- a/components/form/Form.jsx +++ b/components/form/Form.jsx @@ -63,6 +63,7 @@ export const FormProps = { hideRequiredMark: PropTypes.bool, autoFormCreate: PropTypes.func, options: PropTypes.object, + selfUpdate: PropTypes.bool, }; export const ValidationRule = { diff --git a/components/form/FormItem.jsx b/components/form/FormItem.jsx index 1b49d811a..c0a896f83 100644 --- a/components/form/FormItem.jsx +++ b/components/form/FormItem.jsx @@ -34,6 +34,7 @@ export const FormItemProps = { colon: PropTypes.bool, fieldDecoratorId: PropTypes.string, fieldDecoratorOptions: PropTypes.object, + selfUpdate: PropTypes.bool, }; function comeFromSlot(vnodes = [], itemVnode) { let isSlot = false; @@ -69,6 +70,11 @@ export default { data() { return { helpShow: false }; }, + computed: { + itemSelfUpdate() { + return !!(this.selfUpdate === undefined ? this.FormProps.selfUpdate : this.selfUpdate); + }, + }, created() { this.collectContext(); }, @@ -448,7 +454,7 @@ export default { } const option = this.decoratorOption(vnode); if (option && option[0]) { - vnodes[i] = getFieldDecorator(option[0], option[1])(vnode); + vnodes[i] = getFieldDecorator(option[0], option[1], this)(vnode); } } return vnodes; @@ -466,7 +472,7 @@ export default { let child = filterEmpty($slots.default || []); if (decoratorFormProps.form && fieldDecoratorId && child.length) { const getFieldDecorator = decoratorFormProps.form.getFieldDecorator; - child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions)(child[0]); + child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions, this)(child[0]); warning( !(child.length > 1), '`autoFormCreate` just `decorator` then first children. but you can use JSX to support multiple children', diff --git a/components/form/index.zh-CN.md b/components/form/index.zh-CN.md index f9315d214..7dbb19f67 100644 --- a/components/form/index.zh-CN.md +++ b/components/form/index.zh-CN.md @@ -7,6 +7,7 @@ | form | 经 `Form.create()` 包装过的组件会自带 `this.form` 属性,如果使用template语法,可以使用this.$form.createForm(this, options) | object | 无 | | hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false | | layout | 表单布局 | 'horizontal'\|'vertical'\|'inline' | 'horizontal' | +| selfUpdate | 自定义字段更新逻辑,说明[见下](/components/form-cn/#selfUpdate),需1.3.17版本以上 | boolean | false | ### 事件 | 事件名称 | 说明 | 回调参数 | @@ -174,6 +175,7 @@ validateFields(['field1', 'field2'], options, (errors, values) => { | required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false | | validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | | | wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | [object](/components/grid-cn/#Col) | | +| selfUpdate | 自定义字段更新逻辑,你可以通过 Form 的 selfUpdate 进行统一设置。当和 Form 同时设置时,以 Item 为准。 说明[见下](/components/form-cn/#selfUpdate) 需1.3.17版本以上 | boolean | false | ### 校验规则 @@ -194,3 +196,9 @@ validateFields(['field1', 'field2'], options, (errors, values) => { 更多高级用法可研究 [async-validator](https://github.com/yiminghe/async-validator)。 +### selfUpdate + +设置 `selfUpdate` 为 `true` 后,`Form` 通过增量方式更新,只更新被修改的字段。大部分场景下,你只需要编写代码即可。而在某些特定场景,例如修改某个字段值后出现新的字段选项、或者纯粹希望表单任意变化都需要进行渲染。你可以通过修改 Form.Item 取消 selfUpdate,或者在 `change` / `onValuesChange` 回调中手动调用 `this.$forceUpdate()` 更新组件。[示例]() + +如果你并不精通 Vue,并不建议使用 selfUpdate,如果出现性能问题,可以尝试这把 Form 相关的业务独立到一个单独的组件中,减少组件渲染的消耗。 + diff --git a/components/vc-form/src/createBaseForm.jsx b/components/vc-form/src/createBaseForm.jsx index 4fd0776a9..051006814 100644 --- a/components/vc-form/src/createBaseForm.jsx +++ b/components/vc-form/src/createBaseForm.jsx @@ -60,7 +60,7 @@ function createBaseForm(option = {}, mixins = []) { this.instances = {}; this.cachedBind = {}; this.clearedFieldMetaCache = {}; - + this.formItems = {}; this.renderFields = {}; this.domFields = {}; @@ -172,8 +172,9 @@ function createBaseForm(option = {}, mixins = []) { return cache[action].fn; }, - getFieldDecorator(name, fieldOption) { + getFieldDecorator(name, fieldOption, formItem) { const { props, ...restProps } = this.getFieldProps(name, fieldOption); + this.formItems[name] = formItem; return fieldElem => { // We should put field in record if it is rendered this.renderFields[name] = true; @@ -341,17 +342,26 @@ function createBaseForm(option = {}, mixins = []) { setFields(maybeNestedFields, callback) { const fields = this.fieldsStore.flattenRegisteredFields(maybeNestedFields); this.fieldsStore.setFields(fields); + const changedFields = Object.keys(fields).reduce( + (acc, name) => set(acc, name, this.fieldsStore.getField(name)), + {}, + ); if (onFieldsChange) { - const changedFields = Object.keys(fields).reduce( - (acc, name) => set(acc, name, this.fieldsStore.getField(name)), - {}, - ); onFieldsChange(this, changedFields, this.fieldsStore.getNestedAllFields()); } - if (templateContext) { - templateContext.$forceUpdate(); - } else { - this.$forceUpdate(); + const formContext = templateContext || this; + let allUpdate = false; + Object.keys(changedFields).forEach(key => { + let formItem = this.formItems[key]; + formItem = typeof formItem === 'function' ? formItem() : formItem; + if (formItem && formItem.itemSelfUpdate) { + formItem.$forceUpdate(); + } else { + allUpdate = true; + } + }); + if (allUpdate) { + formContext.$forceUpdate(); } this.$nextTick(() => { callback && callback();