feat: support form template

pull/77/merge
tangjinzhou 2018-06-23 17:17:45 +08:00
parent 090f9cba5d
commit 868ee1ebaf
6 changed files with 175 additions and 171 deletions

View File

@ -57,7 +57,8 @@ export const FormProps = {
// onSubmit: React.FormEventHandler<any>; // onSubmit: React.FormEventHandler<any>;
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
hideRequiredMark: PropTypes.bool, hideRequiredMark: PropTypes.bool,
formRef: PropTypes.func, autoFormCreate: PropTypes.func,
options: PropTypes.object,
} }
export const ValidationRule = { export const ValidationRule = {
@ -144,23 +145,6 @@ export default {
fieldDataProp: FIELD_DATA_PROP, 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 () { provide () {
return { return {
FormProps: this.$props, FormProps: this.$props,
@ -179,7 +163,7 @@ export default {
render () { render () {
const { const {
prefixCls, hideRequiredMark, layout, onSubmit, $slots, formRef, prefixCls, hideRequiredMark, layout, onSubmit, $slots, autoFormCreate, options = {},
} = this } = this
const formClassName = classNames(prefixCls, { const formClassName = classNames(prefixCls, {
@ -188,25 +172,45 @@ export default {
[`${prefixCls}-inline`]: layout === 'inline', [`${prefixCls}-inline`]: layout === 'inline',
[`${prefixCls}-hide-required-mark`]: hideRequiredMark, [`${prefixCls}-hide-required-mark`]: hideRequiredMark,
}) })
if (formRef) { if (autoFormCreate) {
const NewForm = createDOMForm({ const saveFormRef = (ref) => {
this.domForm = ref
}
const DomForm = this.DomForm || createDOMForm({
fieldNameProp: 'id', fieldNameProp: 'id',
...options,
fieldMetaProp: FIELD_META_PROP, fieldMetaProp: FIELD_META_PROP,
fieldDataProp: FIELD_DATA_PROP, fieldDataProp: FIELD_DATA_PROP,
templateContext: this.$parent,
})({ })({
provide () { provide () {
return { return {
NewFormProps: this.$props, decoratorFormProps: this.$props,
}
},
data () {
return {
children: $slots.default,
formClassName: formClassName,
submit: onSubmit,
} }
}, },
mounted () { mounted () {
formRef(this.form) autoFormCreate(this.form)
}, },
render () { render () {
return <form onSubmit={onSubmit} class={formClassName}>{$slots.default}</form> const { children, formClassName, submit } = this
return <form onSubmit={submit} class={formClassName}>{children}</form>
}, },
}) })
return <NewForm /> if (this.domForm) {
this.domForm.children = $slots.default
this.domForm.submit = onSubmit
this.domForm.formClassName = formClassName
}
this.DomForm = DomForm
return <DomForm wrappedComponentRef={(inst) => saveFormRef(inst)}/>
} }
return <form onSubmit={onSubmit} class={formClassName}>{$slots.default}</form> return <form onSubmit={onSubmit} class={formClassName}>{$slots.default}</form>

View File

@ -5,7 +5,7 @@ import Row from '../grid/Row'
import Col, { ColProps } from '../grid/Col' import Col, { ColProps } from '../grid/Col'
import warning from '../_util/warning' import warning from '../_util/warning'
import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants' import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants'
import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, getSlots } from '../_util/props-util' import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, getSlots, isEmptyElement } from '../_util/props-util'
import getTransitionProps from '../_util/getTransitionProps' import getTransitionProps from '../_util/getTransitionProps'
import BaseMixin from '../_util/BaseMixin' import BaseMixin from '../_util/BaseMixin'
export const FormItemProps = { export const FormItemProps = {
@ -35,14 +35,14 @@ export default {
}), }),
inject: { inject: {
FormProps: { default: {}}, FormProps: { default: {}},
NewFormProps: { default: {}}, decoratorFormProps: { default: {}},
}, },
data () { data () {
return { helpShow: false } return { helpShow: false }
}, },
mounted () { mounted () {
warning( warning(
this.getControls(this.$slots.default, true).length <= 1, this.getControls(this.slotDefault, true).length <= 1,
'`Form.Item` cannot generate `validateStatus` and `help` automatically, ' + '`Form.Item` cannot generate `validateStatus` and `help` automatically, ' +
'while there are more than one `getFieldDecorator` in it.', 'while there are more than one `getFieldDecorator` in it.',
) )
@ -306,10 +306,10 @@ export default {
) : null ) : null
}, },
renderChildren () { renderChildren () {
// const { $slots, FormProps, NewFormProps, prop } = this // const { $slots, FormProps, decoratorFormProps, prop } = this
// const child = filterEmpty($slots.default || []) // const child = filterEmpty($slots.default || [])
// if (NewFormProps.form && prop && child.length) { // if (decoratorFormProps.form && prop && child.length) {
// const getFieldDecorator = NewFormProps.form.getFieldDecorator // const getFieldDecorator = decoratorFormProps.form.getFieldDecorator
// const rules = FormProps.rules[prop] || [] // const rules = FormProps.rules[prop] || []
// child[0] = getFieldDecorator(prop, { // child[0] = getFieldDecorator(prop, {
// rules, // rules,
@ -344,10 +344,10 @@ export default {
}, },
render () { render () {
const { $slots, NewFormProps, fieldDecoratorId, fieldDecoratorOptions = {}} = this const { $slots, decoratorFormProps, fieldDecoratorId, fieldDecoratorOptions = {}} = this
const child = filterEmpty($slots.default || []) const child = filterEmpty($slots.default || [])
if (NewFormProps.form && fieldDecoratorId && child.length) { if (decoratorFormProps.form && fieldDecoratorId && child.length) {
const getFieldDecorator = NewFormProps.form.getFieldDecorator const getFieldDecorator = decoratorFormProps.form.getFieldDecorator
child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions)(child[0]) child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions)(child[0])
} }
this.slotDefault = child this.slotDefault = child

View File

@ -9,10 +9,49 @@ Use `setFieldsValue` to set other control's value programmaticly.
</us> </us>
<script> <template>
import { Form } from 'vue-antd-ui' <a-form @submit="handleSubmit" :autoFormCreate="(form)=>{this.form = form}">
<a-form-item
label='Note'
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 12 }"
fieldDecoratorId="note"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your note!' }]}"
>
<a-input />
</a-form-item>
<a-form-item
label='Gender'
:labelCol="{ span: 5 }"
:wrapperCol="{ span: 12 }"
fieldDecoratorId="gender"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please select your gender!' }]}"
>
<a-select
placeholder='Select a option and change input text above'
@change="this.handleSelectChange"
>
<a-select-option value='male'>male</a-select-option>
<a-select-option value='female'>female</a-select-option>
</a-select>
</a-form-item>
<a-form-item
:wrapperCol="{ span: 12, offset: 5 }"
>
<a-button type='primary' htmlType='submit'>
Submit
</a-button>
</a-form-item>
</a-form>
</template>
const CoordinatedForm = { <script>
export default {
data () {
return {
formLayout: 'horizontal',
}
},
methods: { methods: {
handleSubmit (e) { handleSubmit (e) {
e.preventDefault() e.preventDefault()
@ -29,54 +68,10 @@ const CoordinatedForm = {
}) })
}, },
}, },
render () {
const { getFieldDecorator } = this.form
return (
<a-form onSubmit={this.handleSubmit}>
<a-form-item
label='Note'
labelCol={{ span: 5 }}
wrapperCol={{ span: 12 }}
>
{getFieldDecorator('note', {
rules: [{ required: true, message: 'Please input your note!' }],
})(
<a-input />
)}
</a-form-item>
<a-form-item
label='Gender'
labelCol={{ span: 5 }}
wrapperCol={{ span: 12 }}
>
{getFieldDecorator('gender', {
rules: [{ required: true, message: 'Please select your gender!' }],
})(
<a-select
placeholder='Select a option and change input text above'
onChange={this.handleSelectChange}
>
<a-select-option value='male'>male</a-select-option>
<a-select-option value='female'>female</a-select-option>
</a-select>
)}
</a-form-item>
<a-form-item
wrapperCol={{ span: 12, offset: 5 }}
>
<a-button type='primary' htmlType='submit'>
Submit
</a-button>
</a-form-item>
</a-form>
)
},
} }
export default Form.create()(CoordinatedForm)
</script> </script>

View File

@ -9,9 +9,39 @@ Perform different check rules according to different situations.
</us> </us>
<script> <template>
import { Form } from 'vue-antd-ui' <a-form :autoFormCreate="(form)=>{this.form = form}">
<a-form-item
:formItemLayout="formItemLayout"
label='Name'
fieldDecoratorId="username"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your name' }]}"
>
<a-input placeholder='Please input your name' />
</a-form-item>
<a-form-item
:formItemLayout="formItemLayout"
label='Nickname'
fieldDecoratorId="nickname"
:fieldDecoratorOptions="{rules: [{ required: checkNick, message: 'Please input your nickname' }]}"
>
<a-input placeholder='Please input your nickname' />
</a-form-item>
<a-form-item :formTailLayout="formTailLayout">
<a-checkbox
:checked="checkNick"
@change="handleChange"
>
Nickname is required
</a-checkbox>
</a-form-item>
<a-form-item :formTailLayout="formTailLayout">
<a-button type='primary' @click="check">Check</a-button>
</a-form-item>
</a-form>
</template>
<script>
const formItemLayout = { const formItemLayout = {
labelCol: { span: 4 }, labelCol: { span: 4 },
wrapperCol: { span: 8 }, wrapperCol: { span: 8 },
@ -20,10 +50,12 @@ const formTailLayout = {
labelCol: { span: 4 }, labelCol: { span: 4 },
wrapperCol: { span: 8, offset: 4 }, wrapperCol: { span: 8, offset: 4 },
} }
const DynamicRule = { export default {
data () { data () {
return { return {
checkNick: false, checkNick: false,
formItemLayout,
formTailLayout,
} }
}, },
methods: { methods: {
@ -43,52 +75,10 @@ const DynamicRule = {
}) })
}, },
}, },
render () {
const { getFieldDecorator } = this.form
return (
<div>
<a-form-item {...{ props: formItemLayout }} label='Name'>
{getFieldDecorator('username', {
rules: [{
required: true,
message: 'Please input your name',
}],
})(
<a-input placeholder='Please input your name' />
)}
</a-form-item>
<a-form-item {...{ props: formItemLayout }} label='Nickname'>
{getFieldDecorator('nickname', {
rules: [{
required: this.checkNick,
message: 'Please input your nickname',
}],
})(
<a-input placeholder='Please input your nickname' />
)}
</a-form-item>
<a-form-item {...{ props: formTailLayout }}>
<a-checkbox
value={this.checkNick}
onChange={this.handleChange}
>
Nickname is required
</a-checkbox>
</a-form-item>
<a-form-item {...{ props: formTailLayout }}>
<a-button type='primary' onClick={this.check}>
Check
</a-button>
</a-form-item>
</div>
)
},
} }
export default Form.create()(DynamicRule)
</script> </script>

View File

@ -1,56 +1,72 @@
<template> <template>
<div> <a-form layout='inline' @submit="handleSubmit" :autoFormCreate="(form)=>{this.form = form}">
<a-form @submit="handleSubmit" :formRef="(form)=>{this.form = form}"> <template v-if="form">
<a-form-item <a-form-item
label='Note' :validateStatus="userNameError() ? 'error' : ''"
:labelCol="{ span: 5 }" :help="userNameError() || ''"
:wrapperCol="{ span: 12 }" fieldDecoratorId="userName"
fieldDecoratorId="note" :fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your username!' }]}"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your note!' }]}"
> >
<a-input /> <a-input placeholder='Username'>
<a-icon slot="prefix" type='user' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
label='Gender' :validateStatus="passwordError() ? 'error' : ''"
:labelCol="{ span: 5 }" :help="passwordError() || ''"
:wrapperCol="{ span: 12 }" fieldDecoratorId="password"
fieldDecoratorId="gender" :fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your Password!' }]}"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please select your gender!' }]}"
> >
<a-select <a-input type='password' placeholder='Password'>
placeholder='Select a option and change input text above' <a-icon slot="prefix" type='lock' style="color:rgba(0,0,0,.25)"/>
@change="this.handleSelectChange" </a-input>
>
<a-select-option value='male'>male</a-select-option>
<a-select-option value='female'>female</a-select-option>
</a-select>
</a-form-item> </a-form-item>
<a-form-item <a-form-item>
:wrapperCol="{ span: 12, offset: 5 }" <a-button
type='primary'
htmlType='submit'
:disabled="hasErrors(form.getFieldsError())"
> >
<a-button type='primary' htmlType='submit'> Log in
Submit
</a-button> </a-button>
</a-form-item> </a-form-item>
</a-form> </template>
</div> </a-form>
</template> </template>
<script> <script>
function hasErrors (fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field])
}
export default { export default {
data () { data () {
return { return {
formLayout: 'horizontal', hasErrors,
rules: { form: null,
test: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}],
},
} }
}, },
mounted () {
},
watch: {
form (val) {
this.$nextTick(() => {
// To disabled submit button at the beginning.
this.form.validateFields()
})
},
},
methods: { methods: {
// Only show error after a field is touched.
userNameError () {
const { getFieldError, isFieldTouched } = this.form
return isFieldTouched('userName') && getFieldError('userName')
},
// Only show error after a field is touched.
passwordError () {
const { getFieldError, isFieldTouched } = this.form
return isFieldTouched('password') && getFieldError('password')
},
handleSubmit (e) { handleSubmit (e) {
e.preventDefault() e.preventDefault()
this.form.validateFields((err, values) => { this.form.validateFields((err, values) => {
@ -59,12 +75,6 @@ export default {
} }
}) })
}, },
handleSelectChange (value) {
console.log(value)
this.form.setFieldsValue({
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
})
},
}, },
} }
</script> </script>

View File

@ -35,6 +35,7 @@ function createBaseForm (option = {}, mixins = []) {
fieldDataProp, fieldDataProp,
formPropName = 'form', formPropName = 'form',
props = {}, props = {},
templateContext,
} = option } = option
return function decorate (WrappedComponent) { return function decorate (WrappedComponent) {
@ -327,7 +328,11 @@ function createBaseForm (option = {}, mixins = []) {
.reduce((acc, name) => set(acc, name, this.fieldsStore.getField(name)), {}) .reduce((acc, name) => set(acc, name, this.fieldsStore.getField(name)), {})
onFieldsChange(this, changedFields, this.fieldsStore.getNestedAllFields()) onFieldsChange(this, changedFields, this.fieldsStore.getNestedAllFields())
} }
if (templateContext) {
templateContext.$forceUpdate()
} else {
this.$forceUpdate() this.$forceUpdate()
}
this.$nextTick(() => { this.$nextTick(() => {
callback && callback() callback && callback()
}) })