feat: update form

pull/77/merge
tjz 2018-06-24 21:18:45 +08:00
parent 868ee1ebaf
commit fdce1ce6b7
12 changed files with 148 additions and 176 deletions

View File

@ -173,9 +173,6 @@ export default {
[`${prefixCls}-hide-required-mark`]: hideRequiredMark,
})
if (autoFormCreate) {
const saveFormRef = (ref) => {
this.domForm = ref
}
const DomForm = this.DomForm || createDOMForm({
fieldNameProp: 'id',
...options,
@ -195,7 +192,7 @@ export default {
submit: onSubmit,
}
},
mounted () {
created () {
autoFormCreate(this.form)
},
render () {
@ -210,7 +207,7 @@ export default {
}
this.DomForm = DomForm
return <DomForm wrappedComponentRef={(inst) => saveFormRef(inst)}/>
return <DomForm wrappedComponentRef={(inst) => { this.domForm = inst }}/>
}
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 warning from '../_util/warning'
import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants'
import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, getSlots, isEmptyElement } from '../_util/props-util'
import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, getSlots } from '../_util/props-util'
import getTransitionProps from '../_util/getTransitionProps'
import BaseMixin from '../_util/BaseMixin'
export const FormItemProps = {
@ -47,10 +47,6 @@ export default {
'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')
@ -306,15 +302,6 @@ export default {
) : null
},
renderChildren () {
// const { $slots, FormProps, decoratorFormProps, prop } = this
// const child = filterEmpty($slots.default || [])
// if (decoratorFormProps.form && prop && child.length) {
// const getFieldDecorator = decoratorFormProps.form.getFieldDecorator
// const rules = FormProps.rules[prop] || []
// child[0] = getFieldDecorator(prop, {
// rules,
// })(child[0])
// }
return [
this.renderLabel(),
this.renderWrapper(
@ -349,7 +336,12 @@ export 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)

View File

@ -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`] = `
<div>
<form class="ant-form ant-form-horizontal">
<div class="ant-row ant-form-item">
<div class="ant-col-4 ant-form-item-label">
<label for="username" title="Name" class="ant-form-item-required">Name</label>
@ -244,7 +244,9 @@ exports[`renders ./components/form/demo/dynamic-rule.vue correctly 1`] = `
</div>
<div class="ant-row ant-form-item">
<div class="ant-col-8 ant-col-offset-4 ant-form-item-control-wrapper">
<div class="ant-form-item-control"><span class="ant-form-item-children"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span>Nickname is required</span></label>
<div class="ant-form-item-control"><span class="ant-form-item-children"><label class="ant-checkbox-wrapper"><span class="ant-checkbox"><input type="checkbox" class="ant-checkbox-input"><span class="ant-checkbox-inner"></span></span><span>
Nickname is required
</span></label>
</span>
<!---->
</div>
@ -258,7 +260,7 @@ exports[`renders ./components/form/demo/dynamic-rule.vue correctly 1`] = `
</div>
</div>
</div>
</div>
</form>
`;
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`] = `
</div>
<div class="ant-row ant-form-item">
<div class="ant-form-item-control-wrapper">
<div class="ant-form-item-control"><span class="ant-form-item-children"><button type="submit" class="ant-btn ant-btn-primary" disabled="disabled"><span>Log in</span></button>
<div class="ant-form-item-control"><span class="ant-form-item-children"><button type="submit" class="ant-btn ant-btn-primary"><span>Log in</span></button>
</span>
<!---->
</div>

View File

@ -13,6 +13,7 @@ Because the width of label is not fixed, you may need to adjust it by customizin
<script>
import { Form } from 'vue-antd-ui'
import { setTimeout } from 'timers'
const AdvancedSearchForm = {
data () {

View File

@ -12,7 +12,8 @@ Perform different check rules according to different situations.
<template>
<a-form :autoFormCreate="(form)=>{this.form = form}">
<a-form-item
:formItemLayout="formItemLayout"
:labelCol="formItemLayout.labelCol"
:wrapperCol="formItemLayout.wrapperCol"
label='Name'
fieldDecoratorId="username"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your name' }]}"
@ -20,14 +21,18 @@ Perform different check rules according to different situations.
<a-input placeholder='Please input your name' />
</a-form-item>
<a-form-item
:formItemLayout="formItemLayout"
:labelCol="formItemLayout.labelCol"
:wrapperCol="formItemLayout.wrapperCol"
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-form-item
:labelCol="formTailLayout.labelCol"
:wrapperCol="formTailLayout.wrapperCol"
>
<a-checkbox
:checked="checkNick"
@change="handleChange"
@ -35,7 +40,10 @@ Perform different check rules according to different situations.
Nickname is required
</a-checkbox>
</a-form-item>
<a-form-item :formTailLayout="formTailLayout">
<a-form-item
:labelCol="formTailLayout.labelCol"
:wrapperCol="formTailLayout.wrapperCol"
>
<a-button type='primary' @click="check">Check</a-button>
</a-form-item>
</a-form>

View File

@ -9,19 +9,70 @@ Horizontal login form is often used in navigation bar.
</us>
<script>
import { Form } from 'vue-antd-ui'
<template>
<a-form layout='inline' @submit="handleSubmit" :autoFormCreate="(form)=>{this.form = form}">
<template v-if="form">
<a-form-item
:validateStatus="userNameError() ? 'error' : ''"
:help="userNameError() || ''"
fieldDecoratorId="userName"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your username!' }]}"
>
<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
:validateStatus="passwordError() ? 'error' : ''"
:help="passwordError() || ''"
fieldDecoratorId="password"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your Password!' }]}"
>
<a-input type='password' placeholder='Password'>
<a-icon slot="prefix" type='lock' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item>
<a-form-item>
<a-button
type='primary'
htmlType='submit'
:disabled="hasErrors(form.getFieldsError())"
>
Log in
</a-button>
</a-form-item>
</template>
</a-form>
</template>
<script>
function hasErrors (fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field])
}
const HorizontalLoginForm = {
export default {
data () {
return {
hasErrors,
form: null,
}
},
mounted () {
// To disabled submit button at the beginning.
this.form.validateFields()
this.$nextTick(() => {
// To disabled submit button at the beginning.
this.form.validateFields()
})
},
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) {
e.preventDefault()
this.form.validateFields((err, values) => {
@ -31,52 +82,10 @@ const HorizontalLoginForm = {
})
},
},
render () {
const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.form
// Only show error after a field is touched.
const userNameError = isFieldTouched('userName') && getFieldError('userName')
const passwordError = isFieldTouched('password') && getFieldError('password')
return (
<a-form layout='inline' onSubmit={this.handleSubmit}>
<a-form-item
validateStatus={userNameError ? 'error' : ''}
help={userNameError || ''}
>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<a-input prefix={<a-icon type='user' style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder='Username' />
)}
</a-form-item>
<a-form-item
validateStatus={passwordError ? 'error' : ''}
help={passwordError || ''}
>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<a-input prefix={<a-icon type='lock' style={{ color: 'rgba(0,0,0,.25)' }} />} type='password' placeholder='Password' />
)}
</a-form-item>
<a-form-item>
<a-button
type='primary'
htmlType='submit'
disabled={hasErrors(getFieldsError())}
>
Log in
</a-button>
</a-form-item>
</a-form>
)
},
}
export default Form.create()(HorizontalLoginForm)
</script>

View File

@ -85,33 +85,40 @@ export default {
return (
<div>
<md cn={md.cn} us={md.us} />
<demo-container code={AdvancedSearchString}>
<AdvancedSearch />
</demo-container>
<demo-container code={CoordinatedString}>
<Coordinated />
</demo-container>
<demo-container code={DynamicRuleString}>
<DynamicRule />
</demo-container>
<demo-container code={HorizontalLoginString}>
<HorizontalLogin />
</demo-container>
<demo-container code={LayoutString}>
<Layout />
</demo-container>
<demo-container code={ValidateStaticString}>
<ValidateStatic />
</demo-container>
<demo-container code={WithoutFormCreateString}>
<WithoutFormCreate />
</demo-container>
<demo-container code={AdvancedSearchString}>
<AdvancedSearch />
</demo-container>
<demo-container code={CustomizedFormControlsString}>
<CustomizedFormControls />
</demo-container>
<demo-container code={DynamicFormItemString}>
<DynamicFormItem />
</demo-container>
<demo-container code={DynamicRuleString}>
<DynamicRule />
</demo-container>
<demo-container code={FormInModalString}>
<FormInModal />
</demo-container>
<demo-container code={GlobalStateString}>
<GlobalState />
</demo-container>
<demo-container code={HorizontalLoginString}>
<HorizontalLogin />
</demo-container>
<demo-container code={LayoutString}>
<Layout />
</demo-container>
<demo-container code={NormalLoginString}>
<NormalLogin />
</demo-container>
@ -121,12 +128,6 @@ export default {
<demo-container code={TimeRelatedControlsString}>
<TimeRelatedControls />
</demo-container>
<demo-container code={ValidateStaticString}>
<ValidateStatic />
</demo-container>
<demo-container code={WithoutFormCreateString}>
<WithoutFormCreate />
</demo-container>
<demo-container code={ValidateOtherString}>
<ValidateOther />
</demo-container>

View File

@ -1,80 +0,0 @@
<template>
<a-form layout='inline' @submit="handleSubmit" :autoFormCreate="(form)=>{this.form = form}">
<template v-if="form">
<a-form-item
:validateStatus="userNameError() ? 'error' : ''"
:help="userNameError() || ''"
fieldDecoratorId="userName"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your username!' }]}"
>
<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
:validateStatus="passwordError() ? 'error' : ''"
:help="passwordError() || ''"
fieldDecoratorId="password"
:fieldDecoratorOptions="{rules: [{ required: true, message: 'Please input your Password!' }]}"
>
<a-input type='password' placeholder='Password'>
<a-icon slot="prefix" type='lock' style="color:rgba(0,0,0,.25)"/>
</a-input>
</a-form-item>
<a-form-item>
<a-button
type='primary'
htmlType='submit'
:disabled="hasErrors(form.getFieldsError())"
>
Log in
</a-button>
</a-form-item>
</template>
</a-form>
</template>
<script>
function hasErrors (fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field])
}
export default {
data () {
return {
hasErrors,
form: null,
}
},
mounted () {
},
watch: {
form (val) {
this.$nextTick(() => {
// To disabled submit button at the beginning.
this.form.validateFields()
})
},
},
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) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values)
}
})
},
},
}
</script>

View File

@ -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
<a-form :autoFormCreate="(form)=>{this.form = form}">
...
</a-form>
````
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

View File

@ -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
<a-form :autoFormCreate="(form)=>{this.form = form}">
...
</a-form>
````
如果使用`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 | {} |
### 校验规则

View File

@ -178,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 =

View File

@ -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/form/demo/test'
import Test from '../components/form/demo/index'
Vue.use(VueClipboard)
Vue.use(VueRouter)