Form 表单
具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。
表单
我们为 form
提供了以下三种排列方式:
- 水平排列:标签和表单控件水平排列;(默认)
- 垂直排列:标签和表单控件上下垂直排列;
- 行内排列:表单项水平行内排列。
表单域
表单一定会包含表单域,表单域可以是输入控件,标准表单域,标签,下拉菜单,文本域等。
这里我们封装了表单域 <Form.Item />
。
注意:
1、如果使用 Form.create
处理表单使其具有自动收集数据并校验的功能,建议使用jsx
。2、如果不是使用Vue.use(Form)形式注册的Form
组件,你需要自行将$form
挂载到Vue原型上。Vue.prototype.$form = Form
代码演示
表单联动
使用 setFieldsValue
来动态设置其他控件的值。
<template>
<a-form
:form="form"
@submit="handleSubmit"
>
<a-form-item
label="Note"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 12 }"
>
<a-input
v-decorator="[
'note',
{rules: [{ required: true, message: 'Please input your note!' }]}
]"
/>
</a-form-item>
<a-form-item
label="Gender"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 12 }"
>
<a-select
v-decorator="[
'gender',
{rules: [{ required: true, message: 'Please select your gender!' }]}
]"
placeholder="Select a option and change input text above"
@change="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
:wrapper-col="{ span: 12, offset: 5 }"
>
<a-button
type="primary"
html-type="submit"
>
Submit
</a-button>
</a-form-item>
</a-form>
</template>
<script>
export default {
data () {
return {
formLayout: 'horizontal',
form: this.$form.createForm(this),
};
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
},
handleSelectChange (value) {
console.log(value);
this.form.setFieldsValue({
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
});
},
},
};
</script>
动态校验规则
根据不同情况执行不同的校验规则。
<template>
<a-form :form="form">
<a-form-item
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
label="Name"
>
<a-input
v-decorator="[
'username',
{rules: [{ required: true, message: 'Please input your name' }]}
]"
placeholder="Please input your name"
/>
</a-form-item>
<a-form-item
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
label="Nickname"
>
<a-input
v-decorator="[
'nickname',
{rules: [{ required: checkNick, message: 'Please input your nickname' }]}
]"
placeholder="Please input your nickname"
/>
</a-form-item>
<a-form-item
:label-col="formTailLayout.labelCol"
:wrapper-col="formTailLayout.wrapperCol"
>
<a-checkbox
:checked="checkNick"
@change="handleChange"
>
Nickname is required
</a-checkbox>
</a-form-item>
<a-form-item
:label-col="formTailLayout.labelCol"
:wrapper-col="formTailLayout.wrapperCol"
>
<a-button
type="primary"
@click="check"
>
Check
</a-button>
</a-form-item>
</a-form>
</template>
<script>
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 8 },
};
const formTailLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 8, offset: 4 },
};
export default {
data () {
return {
checkNick: false,
formItemLayout,
formTailLayout,
form: this.$form.createForm(this),
};
},
methods: {
check () {
this.form.validateFields(
(err) => {
if (!err) {
console.info('success');
}
},
);
},
handleChange (e) {
this.checkNick = e.target.checked;
this.$nextTick(() => {
this.form.validateFields(['nickname'], { force: true });
});
},
},
};
</script>
水平登录栏
水平登录栏,常用在顶部导航栏中。
<template>
<a-form
layout="inline"
:form="form"
@submit="handleSubmit"
>
<a-form-item
:validate-status="userNameError() ? 'error' : ''"
:help="userNameError() || ''"
>
<a-input
v-decorator="[
'userName',
{rules: [{ required: true, message: 'Please input your username!' }]}
]"
placeholder="Username"
>
<a-icon
slot="prefix"
type="user"
style="color:rgba(0,0,0,.25)"
/>
</a-input>
</a-form-item>
<a-form-item
:validate-status="passwordError() ? 'error' : ''"
:help="passwordError() || ''"
>
<a-input
v-decorator="[
'password',
{rules: [{ required: true, message: 'Please input your Password!' }]}
]"
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"
html-type="submit"
:disabled="hasErrors(form.getFieldsError())"
>
Log in
</a-button>
</a-form-item>
</a-form>
</template>
<script>
function hasErrors (fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]);
}
export default {
data () {
return {
hasErrors,
form: this.$form.createForm(this),
};
},
mounted () {
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>
表单布局
表单有三种布局。
<template>
<div>
<a-form :layout="formLayout">
<a-form-item
label="Form Layout"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-radio-group
default-value="horizontal"
@change="handleFormLayoutChange"
>
<a-radio-button value="horizontal">
Horizontal
</a-radio-button>
<a-radio-button value="vertical">
Vertical
</a-radio-button>
<a-radio-button value="inline">
Inline
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item
label="Field A"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input placeholder="input placeholder" />
</a-form-item>
<a-form-item
label="Field B"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input placeholder="input placeholder" />
</a-form-item>
<a-form-item
:wrapper-col="buttonItemLayout.wrapperCol"
>
<a-button type="primary">
Submit
</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script>
export default {
data () {
return {
formLayout: 'horizontal',
};
},
computed: {
formItemLayout () {
const { formLayout } = this;
return formLayout === 'horizontal' ? {
labelCol: { span: 4 },
wrapperCol: { span: 14 },
} : {};
},
buttonItemLayout () {
const { formLayout } = this;
return formLayout === 'horizontal' ? {
wrapperCol: { span: 14, offset: 4 },
} : {};
},
},
methods: {
handleFormLayoutChange (e) {
this.formLayout = e.target.value;
},
},
};
</script>
自定义校验
我们提供了 validateStatus
help
hasFeedback
等属性,你可以不需要使用 Form.create
和 getFieldDecorator
,自己定义校验的时机和内容。
validateStatus
: 校验状态,可选 ‘success’, ‘warning’, ‘error’, ‘validating’。hasFeedback
:用于给输入框添加反馈图标。help
:设置校验文案。
<template>
<a-form>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Fail"
validate-status="error"
help="Should be combination of numbers & alphabets"
>
<a-input
id="error"
placeholder="unavailable choice"
/>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Warning"
validate-status="warning"
>
<a-input
id="warning"
placeholder="Warning"
/>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Validating"
has-feedback
validate-status="validating"
help="The information is being validated..."
>
<a-input
id="validating"
placeholder="I'm the content is being validated"
/>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Success"
has-feedback
validate-status="success"
>
<a-input
id="success"
placeholder="I'm the content"
/>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Warning"
has-feedback
validate-status="warning"
>
<a-input
id="warning"
placeholder="Warning"
/>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Fail"
has-feedback
validate-status="error"
help="Should be combination of numbers & alphabets"
>
<a-input
id="error"
placeholder="unavailable choice"
/>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Success"
has-feedback
validate-status="success"
>
<a-date-picker style="width: 100%" />
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Warning"
has-feedback
validate-status="warning"
>
<a-time-picker style="width: 100%" />
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Error"
has-feedback
validate-status="error"
>
<a-select default-value="1">
<a-select-option value="1">
Option 1
</a-select-option>
<a-select-option value="2">
Option 2
</a-select-option>
<a-select-option value="3">
Option 3
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Validating"
has-feedback
validate-status="validating"
help="The information is being validated..."
>
<a-cascader
:default-value="['1']"
:options="[]"
/>
</a-form-item>
<a-form-item
label="inline"
:label-col="labelCol"
:wrapper-col="wrapperCol"
style="margin-bottom:0;"
>
<a-form-item
validate-status="error"
help="Please select the correct date"
:style="{ display: 'inline-block', width: 'calc(50% - 12px)' }"
>
<a-date-picker style="width: 100%" />
</a-form-item>
<span :style="{ display: 'inline-block', width: '24px', textAlign: 'center' }">
-
</span>
<a-form-item :style="{ display: 'inline-block', width: 'calc(50% - 12px)' }">
<a-date-picker style="width: 100%" />
</a-form-item>
</a-form-item>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Success"
has-feedback
validate-status="success"
>
<a-input-number style="width: 100%" />
</a-form-item>
</a-form>
</template>
<script>
export default {
data () {
return {
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
},
};
},
};
</script>
自行处理表单数据
使用 Form.create
处理后的表单具有自动收集数据并校验的功能,但如果您不需要这个功能,或者默认的行为无法满足业务需求,可以选择不使用 Form.create
并自行处理数据。
<template>
<a-form>
<a-form-item
:label-col="labelCol"
:wrapper-col="wrapperCol"
label="Prime between 8 & 12"
:validate-status="number.validateStatus"
:help="number.errorMsg || tips"
>
<a-input-number
:min="8"
:max="12"
:value="number.value"
@change="handleNumberChange"
/>
</a-form-item>
</a-form>
</template>
<script>
function validatePrimeNumber (number) {
if (number === 11) {
return {
validateStatus: 'success',
errorMsg: null,
};
}
return {
validateStatus: 'error',
errorMsg: 'The prime between 8 and 12 is 11!',
};
}
export default {
data () {
return {
labelCol: { span: 7 },
wrapperCol: { span: 12 },
number: {
value: 11,
},
tips: 'A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.',
};
},
methods: {
handleNumberChange (value) {
this.number = {
...validatePrimeNumber(value),
value,
};
},
},
};
</script>
高级搜索
三列栅格式的表单排列方式,常用于数据表格的高级搜索。有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。
<template>
<div id="components-form-demo-advanced-search">
<a-form
class="ant-advanced-search-form"
:form="form"
@submit="handleSearch"
>
<a-row :gutter="24">
<a-col
v-for="i in 10"
:key="i"
:span="8"
:style="{ display: i < count ? 'block' : 'none' }"
>
<a-form-item :label="`Field ${i}`">
<a-input
v-decorator="[
`field-${i}`,
{
rules: [{
required: true,
message: 'Input something!',
}],
}
]"
placeholder="placeholder"
/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col
:span="24"
:style="{ textAlign: 'right' }"
>
<a-button
type="primary"
html-type="submit"
>
Search
</a-button>
<a-button
:style="{ marginLeft: '8px' }"
@click="handleReset"
>
Clear
</a-button>
<a
:style="{ marginLeft: '8px', fontSize: '12px' }"
@click="toggle"
>
Collapse <a-icon :type="expand ? 'up' : 'down'" />
</a>
</a-col>
</a-row>
</a-form>
<div class="search-result-list">
Search Result List
</div>
</div>
</template>
<script>
export default {
data () {
return {
expand: false,
form: this.$form.createForm(this),
};
},
computed: {
count () {
return this.expand ? 11 : 7;
},
},
methods: {
handleSearch (e) {
e.preventDefault();
this.form.validateFields((error, values) => {
console.log('error', error);
console.log('Received values of form: ', values);
});
},
handleReset () {
this.form.resetFields();
},
toggle () {
this.expand = !this.expand;
},
},
};
</script>
<style>
.ant-advanced-search-form {
padding: 24px;
background: #fbfbfb;
border: 1px solid #d9d9d9;
border-radius: 6px;
}
.ant-advanced-search-form .ant-form-item {
display: flex;
}
.ant-advanced-search-form .ant-form-item-control-wrapper {
flex: 1;
}
#components-form-demo-advanced-search .ant-form {
max-width: none;
}
#components-form-demo-advanced-search .search-result-list {
margin-top: 16px;
border: 1px dashed #e9e9e9;
border-radius: 6px;
background-color: #fafafa;
min-height: 200px;
text-align: center;
padding-top: 80px;
}
</style>
自定义表单控件
自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:
- 提供受控属性
value
或其它与valuePropName
-参数) 的值同名的属性。- 提供
onChange
事件或trigger
-参数) 的值同名的事件。- 不能是函数式组件。
<template>
<a-form
layout="inline"
:form="form"
@submit="handleSubmit"
>
<a-form-item label="Price">
<price-input
v-decorator="[
'price',
{
initialValue: { number: 0, currency: 'rmb' },
rules: [{ validator: checkPrice }],
}
]"
/>
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit"
>
Submit
</a-button>
</a-form-item>
</a-form>
</template>
<script>
const hasProp = (instance, prop) => {
const $options = instance.$options || {};
const propsData = $options.propsData || {};
return prop in propsData;
};
const PriceInput = {
props: ['value'],
template: `
<span>
<a-input
type='text'
:value="number"
@change="handleNumberChange"
style="width: 63%; margin-right: 2%;"
/>
<a-select
:value="currency"
style="width: 32%"
@change="handleCurrencyChange"
>
<a-select-option value='rmb'>RMB</a-select-option>
<a-select-option value='dollar'>Dollar</a-select-option>
</a-select>
</span>
`,
data () {
const value = this.value || {};
return {
number: value.number || 0,
currency: value.currency || 'rmb',
};
},
watch: {
value (val = {}) {
this.number = val.number || 0;
this.currency = val.currency || 'rmb';
},
},
methods: {
handleNumberChange (e) {
const number = parseInt(e.target.value || 0, 10);
if (isNaN(number)) {
return;
}
if (!hasProp(this, 'value')) {
this.number = number;
}
this.triggerChange({ number });
},
handleCurrencyChange (currency) {
if (!hasProp(this, 'value')) {
this.currency = currency;
}
this.triggerChange({ currency });
},
triggerChange (changedValue) {
// Should provide an event to pass value to Form.
this.$emit('change', Object.assign({}, this.$data, changedValue));
},
},
};
export default {
components: {
PriceInput,
},
beforeCreate () {
this.form = this.$form.createForm(this);
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
},
checkPrice (rule, value, callback) {
if (value.number > 0) {
callback();
return;
}
callback('Price must greater than zero!');
},
},
};
</script>
动态增减表单项
动态增加、减少表单项。
<template>
<a-form
:form="form"
@submit="handleSubmit"
>
<a-form-item
v-for="(k, index) in form.getFieldValue('keys')"
:key="k"
v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"
:label="index === 0 ? 'Passengers' : ''"
:required="false"
>
<a-input
v-decorator="[
`names[${k}]`,
{
validateTrigger: ['change', 'blur'],
preserve: true,
rules: [{
required: true,
whitespace: true,
message: 'Please input passenger\'s name or delete this field.',
}],
}
]"
placeholder="passenger name"
style="width: 60%; margin-right: 8px"
/>
<a-icon
v-if="form.getFieldValue('keys').length > 1"
class="dynamic-delete-button"
type="minus-circle-o"
:disabled="form.getFieldValue('keys').length === 1"
@click="() => remove(k)"
/>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button
type="dashed"
style="width: 60%"
@click="add"
>
<a-icon type="plus" /> Add field
</a-button>
</a-form-item>
<a-form-item v-bind="formItemLayoutWithOutLabel">
<a-button
type="primary"
html-type="submit"
>
Submit
</a-button>
</a-form-item>
</a-form>
</template>
<script>
let id = 0;
export default {
data () {
return {
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
},
formItemLayoutWithOutLabel: {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 },
},
},
};
},
beforeCreate () {
this.form = this.$form.createForm(this);
this.form.getFieldDecorator('keys', { initialValue: [], preserve: true });
},
methods: {
remove (k) {
const { form } = this;
// can use data-binding to get
const keys = form.getFieldValue('keys');
// We need at least one passenger
if (keys.length === 1) {
return;
}
// can use data-binding to set
form.setFieldsValue({
keys: keys.filter(key => key !== k),
});
},
add () {
const { form } = this;
// can use data-binding to get
const keys = form.getFieldValue('keys');
const nextKeys = keys.concat(++id);
// can use data-binding to set
// important! notify form to detect changes
form.setFieldsValue({
keys: nextKeys,
});
},
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
},
},
};
</script>
<style>
.dynamic-delete-button {
cursor: pointer;
position: relative;
top: 4px;
font-size: 24px;
color: #999;
transition: all .3s;
}
.dynamic-delete-button:hover {
color: #777;
}
.dynamic-delete-button[disabled] {
cursor: not-allowed;
opacity: 0.5;
}
</style>
弹出层中的新建表单
当用户访问一个展示了某个列表的页面,想新建一项但又不想跳转页面时,可以用 Modal 弹出一个表单,用户填写必要信息后创建新的项。
<template>
<div>
<a-button
type="primary"
@click="showModal"
>
New Collection
</a-button>
<collection-create-form
ref="collectionForm"
:visible="visible"
@cancel="handleCancel"
@create="handleCreate"
/>
</div>
</template>
<script>
const CollectionCreateForm = {
props: ['visible'],
beforeCreate () {
this.form = this.$form.createForm(this);
},
template: `
<a-modal
:visible="visible"
title='Create a new collection'
okText='Create'
@cancel="() => { $emit('cancel') }"
@ok="() => { $emit('create') }"
>
<a-form layout='vertical' :form="form">
<a-form-item label='Title'>
<a-input
v-decorator="[
'title',
{
rules: [{ required: true, message: 'Please input the title of collection!' }],
}
]"
/>
</a-form-item>
<a-form-item label='Description'>
<a-input
type='textarea'
v-decorator="['description']"
/>
</a-form-item>
<a-form-item class='collection-create-form_last-form-item'>
<a-radio-group
v-decorator="[
'modifier',
{
initialValue: 'private',
}
]"
>
<a-radio value='public'>Public</a-radio>
<a-radio value='private'>Private</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
`,
};
export default {
components: { CollectionCreateForm },
data () {
return {
visible: false,
};
},
methods: {
showModal () {
this.visible = true;
},
handleCancel () {
this.visible = false;
},
handleCreate () {
const form = this.$refs.collectionForm.form;
form.validateFields((err, values) => {
if (err) {
return;
}
console.log('Received values of form: ', values);
form.resetFields();
this.visible = false;
});
},
},
};
</script>
表单数据存储于上层组件
通过使用 onFieldsChange
与 mapPropsToFields
,可以把表单的数据存储到上层组件。注意:mapPropsToFields
里面返回的表单域数据必须使用 Form.createFormField
包装。如果你使用Form.create
,上层组件传递的属性,必须在Form.create({ props: …})
的props中声明。如果使用this.$form.createForm
,你可以使用任何数据,不仅仅局限于上层组件的属性。
<template>
<div id="components-form-demo-global-state">
<customized-form
:username="fields.username"
@change="handleFormChange"
/>
<pre class="language-bash">
{{ JSON.stringify(fields, null, 2) }}
</pre>
</div>
</template>
<script>
const CustomizedForm = {
props: ['username'],
template: `
<a-form layout='inline' :form="form">
<a-form-item label='Username'>
<a-input
v-decorator="[
'username',
{
rules: [{ required: true, message: 'Username is required!' }],
}
]"
/>
</a-form-item>
</a-form>
`,
created () {
this.form = this.$form.createForm(this, {
onFieldsChange: (_, changedFields) => {
this.$emit('change', changedFields);
},
mapPropsToFields: () => {
return {
username: this.$form.createFormField({
...this.username,
value: this.username.value,
}),
};
},
onValuesChange (_, values) {
console.log(values);
},
});
},
watch: {
username () {
this.form.updateFields({
username: this.$form.createFormField({
...this.username,
value: this.username.value,
}),
});
},
},
};
export default {
components: {
CustomizedForm,
},
data () {
return {
fields: {
username: {
value: 'benjycui',
},
},
};
},
methods: {
handleFormChange (changedFields) {
console.log('changedFields', changedFields);
this.fields = { ...this.fields, ...changedFields };
},
},
};
</script>
<style>
#components-form-demo-global-state .language-bash {
max-width: 400px;
border-radius: 6px;
margin-top: 24px;
}
</style>
表单数据存储于 Vuex Store 中
通过使用 onFieldsChange 与 mapPropsToFields,可以把表单的数据存储到 Vuex 中。注意:mapPropsToFields
里面返回的表单域数据必须使用 Form.createFormField
包装。
<template>
<div id="components-form-demo-vuex">
<a-form
:form="form"
@submit="handleSubmit"
>
<a-form-item label="Username">
<a-input
v-decorator="[
'username',
{
rules: [{ required: true, message: 'Username is required!' }],
}
]"
/>
</a-form-item>
<a-button
type="primary"
html-type="submit"
>
Submit
</a-button>
</a-form>
</div>
</template>
<script>
export default {
computed: {
username () {
return this.$store.state.username;
},
},
watch: {
username (val) {
console.log('this.$store.state.username: ', val);
this.form.setFieldsValue({username: val});
},
},
created () {
this.form = this.$form.createForm(this, {
onFieldsChange: (_, changedFields) => {
this.$emit('change', changedFields);
},
mapPropsToFields: () => {
return {
username: this.$form.createFormField({
value: this.username,
}),
};
},
onValuesChange: (_, values) =>{
console.log(values);
// Synchronize to vuex store in real time
// this.$store.commit('update', values)
},
});
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
this.$store.commit('update', values);
}
});
},
},
};
</script>
<style>
#components-form-demo-vuex .language-bash {
max-width: 400px;
border-radius: 6px;
margin-top: 24px;
}
</style>
登录框
普通的登录框,可以容纳更多的元素。
<template>
<a-form
id="components-form-demo-normal-login"
:form="form"
class="login-form"
@submit="handleSubmit"
>
<a-form-item>
<a-input
v-decorator="[
'userName',
{ rules: [{ required: true, message: 'Please input your username!' }] }
]"
placeholder="Username"
>
<a-icon
slot="prefix"
type="user"
style="color: rgba(0,0,0,.25)"
/>
</a-input>
</a-form-item>
<a-form-item>
<a-input
v-decorator="[
'password',
{ rules: [{ required: true, message: 'Please input your Password!' }] }
]"
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-checkbox
v-decorator="[
'remember',
{
valuePropName: 'checked',
initialValue: true,
}
]"
>
Remember me
</a-checkbox>
<a
class="login-form-forgot"
href=""
>
Forgot password
</a>
<a-button
type="primary"
html-type="submit"
class="login-form-button"
>
Log in
</a-button>
Or <a href="">
register now!
</a>
</a-form-item>
</a-form>
</template>
<script>
export default {
beforeCreate () {
this.form = this.$form.createForm(this);
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
},
},
};
</script>
<style>
#components-form-demo-normal-login .login-form {
max-width: 300px;
}
#components-form-demo-normal-login .login-form-forgot {
float: right;
}
#components-form-demo-normal-login .login-form-button {
width: 100%;
}
</style>
注册新用户
用户填写必须的信息以注册新用户。
<template>
<a-form
:form="form"
@submit="handleSubmit"
>
<a-form-item
v-bind="formItemLayout"
label="E-mail"
>
<a-input
v-decorator="[
'email',
{
rules: [{
type: 'email', message: 'The input is not valid E-mail!',
}, {
required: true, message: 'Please input your E-mail!',
}]
}
]"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Password"
>
<a-input
v-decorator="[
'password',
{
rules: [{
required: true, message: 'Please input your password!',
}, {
validator: validateToNextPassword,
}],
}
]"
type="password"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Confirm Password"
>
<a-input
v-decorator="[
'confirm',
{
rules: [{
required: true, message: 'Please confirm your password!',
}, {
validator: compareToFirstPassword,
}],
}
]"
type="password"
@blur="handleConfirmBlur"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
>
<span slot="label">
Nickname
<a-tooltip title="What do you want others to call you?">
<a-icon type="question-circle-o" />
</a-tooltip>
</span>
<a-input
v-decorator="[
'nickname',
{
rules: [{ required: true, message: 'Please input your nickname!', whitespace: true }]
}
]"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Habitual Residence"
>
<a-cascader
v-decorator="[
'residence',
{
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
}
]"
:options="residences"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Phone Number"
>
<a-input
v-decorator="[
'phone',
{
rules: [{ required: true, message: 'Please input your phone number!' }],
}
]"
style="width: 100%"
>
<a-select
slot="addonBefore"
v-decorator="[
'prefix',
{ initialValue: '86' }
]"
style="width: 70px"
>
<a-select-option value="86">
+86
</a-select-option>
<a-select-option value="87">
+87
</a-select-option>
</a-select>
</a-input>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Website"
>
<a-auto-complete
v-decorator="[
'website',
{rules: [{ required: true, message: 'Please input website!' }]}
]"
placeholder="website"
@change="handleWebsiteChange"
>
<template slot="dataSource">
<a-select-option
v-for="website in autoCompleteResult"
:key="website"
>
{{ website }}
</a-select-option>
</template>
<a-input />
</a-auto-complete>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Captcha"
extra="We must make sure that your are a human."
>
<a-row :gutter="8">
<a-col :span="12">
<a-input
v-decorator="[
'captcha',
{rules: [{ required: true, message: 'Please input the captcha you got!' }]}
]"
/>
</a-col>
<a-col :span="12">
<a-button>Get captcha</a-button>
</a-col>
</a-row>
</a-form-item>
<a-form-item v-bind="tailFormItemLayout">
<a-checkbox
v-decorator="['agreement', {valuePropName: 'checked'}]"
>
I have read the <a href="">
agreement
</a>
</a-checkbox>
</a-form-item>
<a-form-item v-bind="tailFormItemLayout">
<a-button
type="primary"
html-type="submit"
>
Register
</a-button>
</a-form-item>
</a-form>
</template>
<script>
const residences = [{
value: 'zhejiang',
label: 'Zhejiang',
children: [{
value: 'hangzhou',
label: 'Hangzhou',
children: [{
value: 'xihu',
label: 'West Lake',
}],
}],
}, {
value: 'jiangsu',
label: 'Jiangsu',
children: [{
value: 'nanjing',
label: 'Nanjing',
children: [{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
}],
}],
}];
export default {
data () {
return {
confirmDirty: false,
residences,
autoCompleteResult: [],
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
},
tailFormItemLayout: {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
},
};
},
beforeCreate () {
this.form = this.$form.createForm(this);
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
},
handleConfirmBlur (e) {
const value = e.target.value;
this.confirmDirty = this.confirmDirty || !!value;
},
compareToFirstPassword (rule, value, callback) {
const form = this.form;
if (value && value !== form.getFieldValue('password')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
},
validateToNextPassword (rule, value, callback) {
const form = this.form;
if (value && this.confirmDirty) {
form.validateFields(['confirm'], { force: true });
}
callback();
},
handleWebsiteChange (value) {
let autoCompleteResult;
if (!value) {
autoCompleteResult = [];
} else {
autoCompleteResult = ['.com', '.org', '.net'].map(domain => `${value}${domain}`);
}
this.autoCompleteResult = autoCompleteResult;
},
},
};
</script>
时间类控件
时间类组件的 value
类型为 moment
对象,所以在提交服务器前需要预处理。
<template>
<a-form
:form="form"
@submit="handleSubmit"
>
<a-form-item
v-bind="formItemLayout"
label="DatePicker"
>
<a-date-picker v-decorator="['date-picker', config]" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="DatePicker[showTime]"
>
<a-date-picker
v-decorator="['date-time-picker', config]"
show-time
format="YYYY-MM-DD HH:mm:ss"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="MonthPicker"
>
<a-monthPicker v-decorator="['month-picker', config]" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="RangePicker"
>
<a-range-picker v-decorator="['range-picker', rangeConfig]" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="RangePicker[showTime]"
>
<a-range-picker
v-decorator="['range-time-picker', rangeConfig]"
show-time
format="YYYY-MM-DD HH:mm:ss"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="TimePicker"
>
<a-time-picker v-decorator="['time-picker', config]" />
</a-form-item>
<a-form-item
:wrapper-col="{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
}"
>
<a-button
type="primary"
html-type="submit"
>
Submit
</a-button>
</a-form-item>
</a-form>
</template>
<script>
export default {
data () {
return {
formItemLayout: {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
},
config: {
rules: [{ type: 'object', required: true, message: 'Please select time!' }],
},
rangeConfig: {
rules: [{ type: 'array', required: true, message: 'Please select time!' }],
},
};
},
beforeCreate () {
this.form = this.$form.createForm(this);
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, fieldsValue) => {
if (err) {
return;
}
// Should format date value before submit.
const rangeValue = fieldsValue['range-picker'];
const rangeTimeValue = fieldsValue['range-time-picker'];
const values = {
...fieldsValue,
'date-picker': fieldsValue['date-picker'].format('YYYY-MM-DD'),
'date-time-picker': fieldsValue['date-time-picker'].format('YYYY-MM-DD HH:mm:ss'),
'month-picker': fieldsValue['month-picker'].format('YYYY-MM'),
'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],
'range-time-picker': [
rangeTimeValue[0].format('YYYY-MM-DD HH:mm:ss'),
rangeTimeValue[1].format('YYYY-MM-DD HH:mm:ss'),
],
'time-picker': fieldsValue['time-picker'].format('HH:mm:ss'),
};
console.log('Received values of form: ', values);
});
},
},
};
</script>
校验其他组件
以上演示没有出现的表单控件对应的校验演示。
<template>
<a-form
id="components-form-demo-validate-other"
:form="form"
@submit="handleSubmit"
>
<a-form-item
v-bind="formItemLayout"
label="Plain Text"
>
<span class="ant-form-text">
China
</span>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Select"
has-feedback
>
<a-select
v-decorator="[
'select',
{rules: [{ required: true, message: 'Please select your country!' }]}
]"
placeholder="Please select a country"
>
<a-select-option value="china">
China
</a-select-option>
<a-select-option value="usa">
U.S.A
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Select[multiple]"
>
<a-select
v-decorator="[
'select-multiple', {
rules: [{ required: true, message: 'Please select your favourite colors!', type: 'array' }],
}]"
mode="multiple"
placeholder="Please select favourite colors"
>
<a-select-option value="red">
Red
</a-select-option>
<a-select-option value="green">
Green
</a-select-option>
<a-select-option value="blue">
Blue
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="InputNumber"
>
<a-input-number
v-decorator="['input-number', { initialValue: 3 }]"
:min="1"
:max="10"
/>
<span class="ant-form-text">
machines
</span>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Switch"
>
<a-switch v-decorator="['switch', { valuePropName: 'checked' }]" />
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Slider"
>
<a-slider
v-decorator="['slider']"
:marks="{ 0: 'A', 20: 'B', 40: 'C', 60: 'D', 80: 'E', 100: 'F' }"
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Radio.Group"
>
<a-radio-group v-decorator="['radio-group']">
<a-radio value="a">
item 1
</a-radio>
<a-radio value="b">
item 2
</a-radio>
<a-radio value="c">
item 3
</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Radio.Button"
>
<a-radio-group v-decorator="['radio-button']">
<a-radio-button value="a">
item 1
</a-radio-button>
<a-radio-button value="b">
item 2
</a-radio-button>
<a-radio-button value="c">
item 3
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Checkbox.Group"
>
<a-checkbox-group
v-decorator="['checkbox-group', {initialValue: ['A', 'B']}]"
style="width: 100%;"
>
<a-row>
<a-col :span="8">
<a-checkbox value="A">
A
</a-checkbox>
</a-col>
<a-col :span="8">
<a-checkbox
disabled
value="B"
>
B
</a-checkbox>
</a-col>
<a-col :span="8">
<a-checkbox value="C">
C
</a-checkbox>
</a-col>
<a-col :span="8">
<a-checkbox value="D">
D
</a-checkbox>
</a-col>
<a-col :span="8">
<a-checkbox value="E">
E
</a-checkbox>
</a-col>
</a-row>
</a-checkbox-group>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Rate"
>
<a-rate
v-decorator="['rate', {initialValue: 3.5}]"
allow-half
/>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Upload"
extra="longgggggggggggggggggggggggggggggggggg"
>
<a-upload
v-decorator="['upload', {
valuePropName: 'fileList',
getValueFromEvent: normFile,
}]"
name="logo"
action="/upload.do"
list-type="picture"
>
<a-button>
<a-icon type="upload" /> Click to upload
</a-button>
</a-upload>
</a-form-item>
<a-form-item
v-bind="formItemLayout"
label="Dragger"
>
<div class="dropbox">
<a-upload-dragger
v-decorator="['dragger', {
valuePropName: 'fileList',
getValueFromEvent: normFile,
}]"
name="files"
action="/upload.do"
>
<p class="ant-upload-drag-icon">
<a-icon type="inbox" />
</p>
<p class="ant-upload-text">
Click or drag file to this area to upload
</p>
<p class="ant-upload-hint">
Support for a single or bulk upload.
</p>
</a-upload-dragger>
</div>
</a-form-item>
<a-form-item
:wrapper-col="{ span: 12, offset: 6 }"
>
<a-button
type="primary"
html-type="submit"
>
Submit
</a-button>
</a-form-item>
</a-form>
</template>
<script>
export default {
data: () => ({
formItemLayout: {
labelCol: { span: 6 },
wrapperCol: { span: 14 },
},
}),
beforeCreate () {
this.form = this.$form.createForm(this);
},
methods: {
handleSubmit (e) {
e.preventDefault();
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
},
normFile (e) {
console.log('Upload event:', e);
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
},
},
};
</script>
<style>
#components-form-demo-validate-other .dropbox {
height: 180px;
line-height: 1.5;
}
</style>
API
Form
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
form | 经 Form.create() 包装过的组件会自带 this.form 属性,如果使用template语法,可以使用this.$form.createForm(this, options) | object | 无 |
hideRequiredMark | 隐藏所有表单项的必选标记 | Boolean | false |
layout | 表单布局 | 'horizontal'|'vertical'|'inline' | 'horizontal' |
事件
事件名称 | 说明 | 回调参数 |
---|---|---|
submit | 数据验证成功后回调事件 | Function(e:Event) |
Form.create(options) | this.$form.createForm(this, options)
使用方式如下:
jsx使用方式,使用方式和React版antd一致
const CustomizedForm = {}
CustomizedForm = Form.create({})(CustomizedForm);
如果需要为包装组件实例维护一个ref,可以使用wrappedComponentRef
。
单文件template使用方式
<template>
<a-form :form="form" />
</template>
<script>
export default {
beforeCreate () {
this.form = this.$form.createForm(this, options)
},
}
</script>
options
的配置项如下。
参数 | 说明 | 类型 |
---|---|---|
props | 仅仅支持Form.create({})(CustomizedForm)的使用方式,父组件需要映射到表单项上的属性声明(和vue组件props一致) | {} |
mapPropsToFields | 把父组件的属性映射到表单项上(如:把 Redux store 中的值读出),需要对返回值中的表单域数据用 Form.createFormField 标记,如果使用$form.createForm创建收集器,你可以将任何数据映射到Field中,不受父组件约束 | (props) => ({ [fieldName]: FormField { value } }) |
validateMessages | 默认校验信息,可用于把默认错误信息改为中文等,格式与 newMessages 返回值一致 | Object { [nested.path]: String } |
onFieldsChange | 当 Form.Item 子节点的值发生改变时触发,可以把对应的值转存到 Redux store | Function(props, fields) |
onValuesChange | 任一表单域的值发生改变时的回调 | (props, values) => void |
经过 Form.create
包装的组件将会自带 this.form
属性,this.form
提供的 API 如下:
注意:使用
getFieldsValue
getFieldValue
setFieldsValue
等时,应确保对应的 field 已经用getFieldDecorator
或v-decorator
注册过了。
方法 | 说明 | 类型 |
---|---|---|
getFieldDecorator | 用于和表单进行双向绑定,单文件template可以使用指令v-decorator 进行绑定,详见下方描述 | |
getFieldError | 获取某个输入控件的 Error | Function(name) |
getFieldsError | 获取一组输入控件的 Error ,如不传入参数,则获取全部组件的 Error | Function([names: string[]]) |
getFieldsValue | 获取一组输入控件的值,如不传入参数,则获取全部组件的值 | Function([fieldNames: string[]]) |
getFieldValue | 获取一个输入控件的值 | Function(fieldName: string) |
isFieldsTouched | 判断是否任一输入控件经历过 getFieldDecorator 或 v-decorator 的值收集时机 options.trigger | (names?: string[]) => boolean |
isFieldTouched | 判断一个输入控件是否经历过 getFieldDecorator 或 v-decorator 的值收集时机 options.trigger | (name: string) => boolean |
isFieldValidating | 判断一个输入控件是否在校验状态 | Function(name) |
resetFields | 重置一组输入控件的值(为 initialValue )与状态,如不传入参数,则重置所有组件 | Function([names: string[]]) |
setFields | 设置一组输入控件的值与错误状态。 | Function({ [fieldName]: { value: any, errors: [Error] } }) |
setFieldsValue | 设置一组输入控件的值 | Function({ [fieldName]: value } |
validateFields | 校验并获取一组输入域的值与 Error,若 fieldNames 参数为空,则校验全部组件 | Function([fieldNames: string[]], [options: object], callback: Function(errors, values)) |
validateFieldsAndScroll | 与 validateFields 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围 | 参考 validateFields |
validateFields/validateFieldsAndScroll
const { form: { validateFields } } = this;
validateFields((errors, values) => {
// ...
});
validateFields(['field1', 'field2'], (errors, values) => {
// ...
});
validateFields(['field1', 'field2'], options, (errors, values) => {
// ...
});
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
options.first | 若为 true,则每一表单域的都会在碰到第一个失败了的校验规则后停止校验 | boolean | false |
options.firstFields | 指定表单域会在碰到第一个失败了的校验规则后停止校验 | String[] | [] |
options.force | 对已经校验过的表单域,在 validateTrigger 再次被触发时是否再次校验 | boolean | false |
options.scroll | 定义 validateFieldsAndScroll 的滚动行为,详细配置见 dom-scroll-into-view config | Object | {} |
validateFields 的 callback 参数示例
errors
:
{
"userName": {
"errors": [
{
"message": "Please input your username!",
"field": "userName"
}
]
},
"password": {
"errors": [
{
"message": "Please input your Password!",
"field": "password"
}
]
}
}
values
:
{
"userName": "username",
"password": "password",
}
Form.createFormField
用于标记 mapPropsToFields
返回的表单域数据,例子。
this.form.getFieldDecorator(id, options) 和 v-decorator="[id, options]"
经过 getFieldDecorator
或v-decorator
包装的控件,表单控件会自动添加 value
(或 valuePropName
指定的其他属性) onChange
(或 trigger
指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
- 你不再需要也不应该用
onChange
来做同步,但还是可以继续监听onChange
等事件。 - 你不能用控件的
value
defaultValue
等属性来设置表单域的值,默认值可以用getFieldDecorator
或v-decorator
里的initialValue
。 - 你不应该用
v-model
,可以使用this.form.setFieldsValue
来动态改变表单值。
特别注意
getFieldDecorator
和v-decorator
不能用于装饰纯函数组件。getFieldDecorator
和v-decorator
调用不能位于纯函数组件中 https://cn.vuejs.org/v2/api/#functional。
getFieldDecorator(id, options) 和 v-decorator="[id, options]" 参数
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
id | 必填输入控件唯一标志。支持嵌套式的写法。 | string | |
options.getValueFromEvent | 可以把 onChange 的参数(如 event)转化为控件的值 | function(..args) | reference |
options.initialValue | 子节点的初始值,类型、可选值均由子节点决定(注意:由于内部校验时使用 === 判断是否变化,建议使用变量缓存所需设置的值而非直接使用字面量)) | ||
options.normalize | 转换默认的 value 给控件,一个选择全部的例子 | function(value, prevValue, allValues): any | - |
options.preserve | 即便字段不再使用,也保留该字段的值 | boolean | false |
options.rules | 校验规则,参考下方文档 | object[] | |
options.trigger | 收集子节点的值的时机 | string | 'change' |
options.validateFirst | 当某一规则校验不通过时,是否停止剩下的规则的校验 | boolean | false |
options.validateTrigger | 校验子节点值的时机 | string|string[] | 'change' |
options.valuePropName | 子节点的值的属性,如 Switch 的是 'checked' | string | 'value' |
Form.Item
注意:一个 Form.Item 建议只放一个被 getFieldDecorator或v-decorator 装饰过的 child,当有多个被装饰过的 child 时,help
required
validateStatus
无法自动生成。
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
colon | 配合 label 属性使用,表示是否显示 label 后面的冒号 | boolean | true |
extra | 额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 | string|slot | |
hasFeedback | 配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用 | boolean | false |
help | 提示信息,如不设置,则会根据校验规则自动生成 | string|slot | |
label | label 标签的文本 | string|slot | |
labelCol | label 标签布局,同 <Col> 组件,设置 span offset 值,如 {span: 3, offset: 12} 或 sm: {span: 3, offset: 12} | object | |
required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false |
validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | |
wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol | object |
校验规则
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
enum | 枚举类型 | string | - |
len | 字段长度 | number | - |
max | 最大长度 | number | - |
message | 校验文案 | string | - |
min | 最小长度 | number | - |
pattern | 正则表达式校验 | RegExp | - |
required | 是否必选 | boolean | false |
transform | 校验前转换字段值 | function(value) => transformedValue:any | - |
type | 内建校验类型,可选项 | string | 'string' |
validator | 自定义校验(注意,callback 必须被调用) | function(rule, value, callback) | - |
whitespace | 必选时,空格是否会被视为错误 | boolean | false |
更多高级用法可研究 async-validator。