From 6cad9c3cc0ceec267f9de662633ea840aac0d25c Mon Sep 17 00:00:00 2001
From: undefined <undefined>
Date: Mon, 6 Jul 2020 22:31:07 +0800
Subject: [PATCH] feat: udpate formmodel

---
 components/_util/vnode.js          | 12 +++--
 components/form-model/Form.jsx     | 22 +++++----
 components/form-model/FormItem.jsx | 74 ++++++++++++++---------------
 components/form-model/index.jsx    | 14 ++----
 components/form/FormItem.jsx       | 75 ++++++++++++++----------------
 examples/index.js                  |  6 +++
 6 files changed, 101 insertions(+), 102 deletions(-)

diff --git a/components/_util/vnode.js b/components/_util/vnode.js
index 42cd099a6..4540b797a 100644
--- a/components/_util/vnode.js
+++ b/components/_util/vnode.js
@@ -1,10 +1,10 @@
 import { filterEmpty } from './props-util';
 import { cloneVNode } from 'vue';
 
-export function cloneElement(n, nodeProps = {}, override = true) {
-  let ele = n;
-  if (Array.isArray(n)) {
-    ele = filterEmpty(n)[0];
+export function cloneElement(vnode, nodeProps = {}, override = true) {
+  let ele = vnode;
+  if (Array.isArray(vnode)) {
+    ele = filterEmpty(vnode)[0];
   }
   if (!ele) {
     return null;
@@ -15,3 +15,7 @@ export function cloneElement(n, nodeProps = {}, override = true) {
   node.props = override ? { ...node.props, ...nodeProps } : node.props;
   return node;
 }
+
+export function cloneVNodes(vnodes, nodeProps = {}, override = true) {
+  return vnodes.map(vnode => cloneElement(vnode, nodeProps, override));
+}
diff --git a/components/form-model/Form.jsx b/components/form-model/Form.jsx
index de6fcdcdd..2e5f8bfa7 100755
--- a/components/form-model/Form.jsx
+++ b/components/form-model/Form.jsx
@@ -1,10 +1,11 @@
+import { inject, provide } from 'vue';
 import PropTypes from '../_util/vue-types';
 import classNames from 'classnames';
 import { ColProps } from '../grid/Col';
 import isRegExp from 'lodash/isRegExp';
 import warning from '../_util/warning';
 import FormItem from './FormItem';
-import { initDefaultProps, getListeners } from '../_util/props-util';
+import { initDefaultProps, getListeners, getSlot } from '../_util/props-util';
 import { ConfigConsumerProps } from '../config-provider';
 
 export const FormProps = {
@@ -48,6 +49,7 @@ export const ValidationRule = {
 
 const Form = {
   name: 'AFormModel',
+  inheritAttrs: false,
   props: initDefaultProps(FormProps, {
     layout: 'horizontal',
     hideRequiredMark: false,
@@ -56,15 +58,14 @@ const Form = {
   Item: FormItem,
   created() {
     this.fields = [];
+    this.form = undefined;
+    provide('FormContext', this);
   },
-  provide() {
+  setup() {
     return {
-      FormContext: this,
+      configProvider: inject('configProvider', ConfigConsumerProps),
     };
   },
-  inject: {
-    configProvider: { default: () => ConfigConsumerProps },
-  },
   watch: {
     rules() {
       if (this.validateOnRuleChange) {
@@ -164,19 +165,20 @@ const Form = {
   },
 
   render() {
-    const { prefixCls: customizePrefixCls, hideRequiredMark, layout, onSubmit, $slots } = this;
+    const { prefixCls: customizePrefixCls, hideRequiredMark, layout, onSubmit } = this;
     const getPrefixCls = this.configProvider.getPrefixCls;
     const prefixCls = getPrefixCls('form', customizePrefixCls);
+    const { class: className, onSubmit: originSubmit, ...restProps } = this.$attrs;
 
-    const formClassName = classNames(prefixCls, {
+    const formClassName = classNames(prefixCls, className, {
       [`${prefixCls}-horizontal`]: layout === 'horizontal',
       [`${prefixCls}-vertical`]: layout === 'vertical',
       [`${prefixCls}-inline`]: layout === 'inline',
       [`${prefixCls}-hide-required-mark`]: hideRequiredMark,
     });
     return (
-      <form onSubmit={onSubmit} class={formClassName}>
-        {$slots.default}
+      <form onSubmit={onSubmit} class={formClassName} {...restProps}>
+        {getSlot(this)}
       </form>
     );
   },
diff --git a/components/form-model/FormItem.jsx b/components/form-model/FormItem.jsx
index aaf1db79c..38acbc472 100644
--- a/components/form-model/FormItem.jsx
+++ b/components/form-model/FormItem.jsx
@@ -1,14 +1,15 @@
+import { inject } from 'vue';
 import AsyncValidator from 'async-validator';
 import cloneDeep from 'lodash/cloneDeep';
 import PropTypes from '../_util/vue-types';
 import { ColProps } from '../grid/Col';
 import {
   initDefaultProps,
-  getComponentFromProp,
+  getComponent,
   getOptionProps,
   getEvents,
-  filterEmpty,
   isValidElement,
+  getSlot,
 } from '../_util/props-util';
 import BaseMixin from '../_util/BaseMixin';
 import { ConfigConsumerProps } from '../config-provider';
@@ -63,15 +64,18 @@ export const FormItemProps = {
 
 export default {
   name: 'AFormModelItem',
-  __ANT_NEW_FORM_ITEM: true,
   mixins: [BaseMixin],
+  inheritAttrs: false,
+  __ANT_NEW_FORM_ITEM: true,
   props: initDefaultProps(FormItemProps, {
     hasFeedback: false,
     autoLink: true,
   }),
-  inject: {
-    configProvider: { default: () => ConfigConsumerProps },
-    FormContext: { default: () => ({}) },
+  setup() {
+    return {
+      configProvider: inject('configProvider', ConfigConsumerProps),
+      FormContext: inject('FormContext', {}),
+    };
   },
   data() {
     return {
@@ -216,43 +220,39 @@ export default {
     },
   },
   render() {
-    const { $slots, $scopedSlots } = this;
-    const props = getOptionProps(this);
-    const label = getComponentFromProp(this, 'label');
-    const extra = getComponentFromProp(this, 'extra');
-    const help = getComponentFromProp(this, 'help');
+    const { autoLink, ...props } = getOptionProps(this);
+    const label = getComponent(this, 'label');
+    const extra = getComponent(this, 'extra');
+    const help = getComponent(this, 'help');
     const formProps = {
-      props: {
-        ...props,
-        label,
-        extra,
-        validateStatus: this.validateState,
-        help: this.validateMessage || help,
-        required: this.isRequired || props.required,
-      },
+      ...this.$attrs,
+      ...props,
+      label,
+      extra,
+      validateStatus: this.validateState,
+      help: this.validateMessage || help,
+      required: this.isRequired || props.required,
     };
-    const children = filterEmpty($scopedSlots.default ? $scopedSlots.default() : $slots.default);
+    const children = getSlot(this);
     let firstChildren = children[0];
-    if (this.prop && this.autoLink && isValidElement(firstChildren)) {
+    if (this.prop && autoLink && isValidElement(firstChildren)) {
       const originalEvents = getEvents(firstChildren);
-      const originalBlur = originalEvents.blur;
-      const originalChange = originalEvents.change;
+      const originalBlur = originalEvents.onBlur;
+      const originalChange = originalEvents.onChange;
       firstChildren = cloneElement(firstChildren, {
-        on: {
-          blur: (...args) => {
-            originalBlur && originalBlur(...args);
-            this.onFieldBlur();
-          },
-          change: (...args) => {
-            if (Array.isArray(originalChange)) {
-              for (let i = 0, l = originalChange.length; i < l; i++) {
-                originalChange[i](...args);
-              }
-            } else if (originalChange) {
-              originalChange(...args);
+        onBlur: (...args) => {
+          originalBlur && originalBlur(...args);
+          this.onFieldBlur();
+        },
+        onChange: (...args) => {
+          if (Array.isArray(originalChange)) {
+            for (let i = 0, l = originalChange.length; i < l; i++) {
+              originalChange[i](...args);
             }
-            this.onFieldChange();
-          },
+          } else if (originalChange) {
+            originalChange(...args);
+          }
+          this.onFieldChange();
         },
       });
     }
diff --git a/components/form-model/index.jsx b/components/form-model/index.jsx
index afc701b07..b63f33cf5 100644
--- a/components/form-model/index.jsx
+++ b/components/form-model/index.jsx
@@ -1,20 +1,12 @@
-import Vue from 'vue';
 import Form from './Form';
-import ref from 'vue-ref';
-import FormDecoratorDirective from '../_util/FormDecoratorDirective';
-import Base from '../base';
-
-Vue.use(ref, { name: 'ant-ref' });
-Vue.use(FormDecoratorDirective);
 
 export { FormProps, ValidationRule } from './Form';
 export { FormItemProps } from './FormItem';
 
 /* istanbul ignore next */
-Form.install = function(Vue) {
-  Vue.use(Base);
-  Vue.component(Form.name, Form);
-  Vue.component(Form.Item.name, Form.Item);
+Form.install = function(app) {
+  app.component(Form.name, Form);
+  app.component(Form.Item.name, Form.Item);
 };
 
 export default Form;
diff --git a/components/form/FormItem.jsx b/components/form/FormItem.jsx
index 2b04268c1..64744fb08 100644
--- a/components/form/FormItem.jsx
+++ b/components/form/FormItem.jsx
@@ -1,3 +1,4 @@
+import { provide, inject, Transition } from 'vue';
 import PropTypes from '../_util/vue-types';
 import classNames from 'classnames';
 import find from 'lodash/find';
@@ -7,11 +8,12 @@ import warning from '../_util/warning';
 import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants';
 import {
   initDefaultProps,
-  getComponentFromProp,
-  filterEmpty,
+  getComponent,
   getSlotOptions,
   isValidElement,
   getAllChildren,
+  findDOMNode,
+  getSlot,
 } from '../_util/props-util';
 import getTransitionProps from '../_util/getTransitionProps';
 import BaseMixin from '../_util/BaseMixin';
@@ -73,23 +75,21 @@ function comeFromSlot(vnodes = [], itemVnode) {
 
 export default {
   name: 'AFormItem',
-  __ANT_FORM_ITEM: true,
   mixins: [BaseMixin],
+  inheritAttrs: false,
+  __ANT_FORM_ITEM: true,
   props: initDefaultProps(FormItemProps, {
     hasFeedback: false,
   }),
-  provide() {
+  setup() {
     return {
-      isFormItemChildren: true,
+      isFormItemChildren: inject('isFormItemChildren', false),
+      FormContext: inject('FormContext', {}),
+      decoratorFormProps: inject('decoratorFormProps', {}),
+      collectFormItemContext: inject('collectFormItemContext', noop),
+      configProvider: inject('configProvider', ConfigConsumerProps),
     };
   },
-  inject: {
-    isFormItemChildren: { default: false },
-    FormContext: { default: () => ({}) },
-    decoratorFormProps: { default: () => ({}) },
-    collectFormItemContext: { default: () => noop },
-    configProvider: { default: () => ConfigConsumerProps },
-  },
   data() {
     return { helpShow: false };
   },
@@ -99,6 +99,7 @@ export default {
     },
   },
   created() {
+    provide('isFormItemChildren', true);
     this.collectContext();
   },
   beforeUpdate() {
@@ -145,7 +146,7 @@ export default {
       }
     },
     getHelpMessage() {
-      const help = getComponentFromProp(this, 'help');
+      const help = getComponent(this, 'help');
       const onlyControl = this.getOnlyControl();
       if (help === undefined && onlyControl) {
         const errors = this.getField().errors;
@@ -177,15 +178,15 @@ export default {
         }
 
         const child = childrenArray[i];
-        if (!child.tag && child.text.trim() === '') {
-          continue;
-        }
+        // if (!child.tag && child.text.trim() === '') {
+        //   continue;
+        // }
 
-        if (getSlotOptions(child).__ANT_FORM_ITEM) {
+        if (typeof child.type === 'object' && child.type.__ANT_FORM_ITEM) {
           continue;
         }
         const children = getAllChildren(child);
-        const attrs = (child.data && child.data.attrs) || {};
+        const attrs = child.props || {};
         if (FIELD_META_PROP in attrs) {
           // And means FIELD_DATA_PROP in child.props, too.
           controls.push(child);
@@ -207,6 +208,7 @@ export default {
       if (!child) {
         return undefined;
       }
+      debugger;
       if (child.data) {
         data = child.data;
       } else if (child.$vnode && child.$vnode.data) {
@@ -253,7 +255,7 @@ export default {
       if (!id) {
         return;
       }
-      const formItemNode = this.$el;
+      const formItemNode = findDOMNode(this);
       const control = formItemNode.querySelector(`[id="${id}"]`);
       if (control && control.focus) {
         control.focus();
@@ -296,18 +298,18 @@ export default {
         this.helpShow = !!children;
       }
       const transitionProps = getTransitionProps('show-help', {
-        afterEnter: () => this.onHelpAnimEnd('help', true),
-        afterLeave: () => this.onHelpAnimEnd('help', false),
+        onAfterEnter: () => this.onHelpAnimEnd('help', true),
+        onAfterLeave: () => this.onHelpAnimEnd('help', false),
       });
       return (
-        <transition {...transitionProps} key="help">
+        <Transition {...transitionProps} key="help">
           {children}
-        </transition>
+        </Transition>
       );
     },
 
     renderExtra(prefixCls) {
-      const extra = getComponentFromProp(this, 'extra');
+      const extra = getComponent(this, 'extra');
       return extra ? <div class={`${prefixCls}-extra`}>{extra}</div> : null;
     },
 
@@ -353,15 +355,14 @@ export default {
       const { wrapperCol: contextWrapperCol } = this.isFormItemChildren ? {} : this.FormContext;
       const { wrapperCol } = this;
       const mergedWrapperCol = wrapperCol || contextWrapperCol || {};
-      const { style, id, on, ...restProps } = mergedWrapperCol;
+      const { style, id, ...restProps } = mergedWrapperCol;
       const className = classNames(`${prefixCls}-item-control-wrapper`, mergedWrapperCol.class);
       const colProps = {
-        props: restProps,
+        ...restProps,
         class: className,
         key: 'wrapper',
         style,
         id,
-        on,
       };
       return <Col {...colProps}>{children}</Col>;
     },
@@ -374,7 +375,7 @@ export default {
         colon: contextColon,
       } = this.FormContext;
       const { labelAlign, labelCol, colon, id, htmlFor } = this;
-      const label = getComponentFromProp(this, 'label');
+      const label = getComponent(this, 'label');
       const required = this.isRequired();
       const mergedLabelCol = labelCol || contextLabelCol || {};
 
@@ -389,7 +390,6 @@ export default {
         class: labelColClass,
         style: labelColStyle,
         id: labelColId,
-        on,
         ...restProps
       } = mergedLabelCol;
       let labelChildren = label;
@@ -406,12 +406,11 @@ export default {
         [`${prefixCls}-item-no-colon`]: !computedColon,
       });
       const colProps = {
-        props: restProps,
+        ...restProps,
         class: labelColClassName,
         key: 'label',
         style: labelColStyle,
         id: labelColId,
-        on,
       };
 
       return label ? (
@@ -443,16 +442,18 @@ export default {
     },
     renderFormItem() {
       const { prefixCls: customizePrefixCls } = this.$props;
+      const { class: className, ...restProps } = this.$attrs;
       const getPrefixCls = this.configProvider.getPrefixCls;
       const prefixCls = getPrefixCls('form', customizePrefixCls);
       const children = this.renderChildren(prefixCls);
       const itemClassName = {
+        [className]: true,
         [`${prefixCls}-item`]: true,
         [`${prefixCls}-item-with-help`]: this.helpShow,
       };
 
       return (
-        <Row class={classNames(itemClassName)} key="row">
+        <Row class={classNames(itemClassName)} key="row" {...restProps}>
           {children}
         </Row>
       );
@@ -497,14 +498,8 @@ export default {
   },
 
   render() {
-    const {
-      $slots,
-      decoratorFormProps,
-      fieldDecoratorId,
-      fieldDecoratorOptions = {},
-      FormContext,
-    } = this;
-    let child = filterEmpty($slots.default || []);
+    const { decoratorFormProps, fieldDecoratorId, fieldDecoratorOptions = {}, FormContext } = this;
+    let child = getSlot(this);
     if (decoratorFormProps.form && fieldDecoratorId && child.length) {
       const getFieldDecorator = decoratorFormProps.form.getFieldDecorator;
       child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions, this)(child[0]);
diff --git a/examples/index.js b/examples/index.js
index fa856fc9d..4b0454161 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -17,6 +17,9 @@ import {
   Tooltip,
   Col,
   Row,
+  FormModel,
+  Switch,
+  Checkbox,
   notification,
   message,
 } from 'ant-design-vue';
@@ -49,6 +52,9 @@ app
   .use(Col)
   .use(Row)
   .use(Radio)
+  .use(Switch)
+  .use(Checkbox)
   .use(InputNumber)
   .use(AutoComplete)
+  .use(FormModel)
   .mount('#app');