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 ( -
- {$slots.default} + + {getSlot(this)}
); }, 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 ( - + {children} - + ); }, renderExtra(prefixCls) { - const extra = getComponentFromProp(this, 'extra'); + const extra = getComponent(this, 'extra'); return extra ?
{extra}
: 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 {children}; }, @@ -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 ( - + {children} ); @@ -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');