Form 表单
概述
具有数据收集、校验和提交功能的表单,包含复选框、单选框、输入框、下拉选择框等元素。
注意:非 template/render 模式下,需使用 i-form
。
W3C 标准中有如下规定:
When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.
即:当一个 form 元素中只有一个输入框时,在该输入框中按下回车应提交该表单。如果希望阻止这一默认行为,可以在 <Form>
标签上添加 @submit.native.prevent
。
代码示例
设置属性 inline
,表单元素可以水平排列。
<template>
<Form ref="formInline" :model="formInline" :rules="ruleInline" inline>
<FormItem prop="user">
<Input type="text" v-model="formInline.user" placeholder="Username">
<Icon type="ios-person-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="password">
<Input type="password" v-model="formInline.password" placeholder="Password">
<Icon type="ios-lock-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem>
<Button type="primary" @click="handleSubmit('formInline')">Signin</Button>
</FormItem>
</Form>
</template>
<script>
export default {
data () {
return {
formInline: {
user: '',
password: ''
},
ruleInline: {
user: [
{ required: true, message: 'Please fill in the user name', trigger: 'blur' }
],
password: [
{ required: true, message: 'Please fill in the password.', trigger: 'blur' },
{ type: 'string', min: 6, message: 'The password length cannot be less than 6 bits', trigger: 'blur' }
]
}
}
},
methods: {
handleSubmit(name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.$Message.success('Success!');
} else {
this.$Message.error('Fail!');
}
})
}
}
}
</script>
在 Form
内,每个表单域由 FormItem
组成,可包含的控件有:Input、Radio、Checkbox、Switch、Select、Slider、DatePicker、TimePicker、Cascader、Transfer、InputNumber、Rate、Upload、AutoComplete、ColorPicker。
给 FormItem
设置属性 label
可以显示表单域的标签,需要给 Form 设置 label-width
。
给 FormItem
设置属性 label-for
可以指定原生的 label 标签的 for 属性,配合设置控件的 element-id
属性,可以点击 label 时聚焦控件。
<template>
<Form :model="formItem" :label-width="80">
<FormItem label="Input">
<Input v-model="formItem.input" placeholder="Enter something..."></Input>
</FormItem>
<FormItem label="Select">
<Select v-model="formItem.select">
<Option value="beijing">New York</Option>
<Option value="shanghai">London</Option>
<Option value="shenzhen">Sydney</Option>
</Select>
</FormItem>
<FormItem label="DatePicker">
<Row>
<Col span="11">
<DatePicker type="date" placeholder="Select date" v-model="formItem.date"></DatePicker>
</Col>
<Col span="2" style="text-align: center">-</Col>
<Col span="11">
<TimePicker type="time" placeholder="Select time" v-model="formItem.time"></TimePicker>
</Col>
</Row>
</FormItem>
<FormItem label="Radio">
<RadioGroup v-model="formItem.radio">
<Radio label="male">Male</Radio>
<Radio label="female">Female</Radio>
</RadioGroup>
</FormItem>
<FormItem label="Checkbox">
<CheckboxGroup v-model="formItem.checkbox">
<Checkbox label="Eat"></Checkbox>
<Checkbox label="Sleep"></Checkbox>
<Checkbox label="Run"></Checkbox>
<Checkbox label="Movie"></Checkbox>
</CheckboxGroup>
</FormItem>
<FormItem label="Switch">
<i-switch v-model="formItem.switch" size="large">
<span slot="open">On</span>
<span slot="close">Off</span>
</i-switch>
</FormItem>
<FormItem label="Slider">
<Slider v-model="formItem.slider" range></Slider>
</FormItem>
<FormItem label="Text">
<Input v-model="formItem.textarea" type="textarea" :autosize="{minRows: 2,maxRows: 5}" placeholder="Enter something..."></Input>
</FormItem>
<FormItem>
<Button type="primary">Submit</Button>
<Button style="margin-left: 8px">Cancel</Button>
</FormItem>
</Form>
</template>
<script>
export default {
data () {
return {
formItem: {
input: '',
select: '',
radio: 'male',
checkbox: [],
switch: true,
date: '',
time: '',
slider: [20, 50],
textarea: ''
}
}
}
}
</script>
设置属性 label-position
,可以改变表单域标签的位置,left 为左对齐,right 为右对齐,top 会置于表单域顶部。
<template>
<Form :model="formLeft" label-position="left" :label-width="100">
<FormItem label="Title">
<Input v-model="formLeft.input1"></Input>
</FormItem>
<FormItem label="Title name">
<Input v-model="formLeft.input2"></Input>
</FormItem>
<FormItem label="Aligned title">
<Input v-model="formLeft.input3"></Input>
</FormItem>
</Form>
<Form :model="formRight" label-position="right" :label-width="100">
<FormItem label="Title">
<Input v-model="formRight.input1"></Input>
</FormItem>
<FormItem label="Title name">
<Input v-model="formRight.input2"></Input>
</FormItem>
<FormItem label="Aligned title">
<Input v-model="formRight.input3"></Input>
</FormItem>
</Form>
<Form :model="formTop" label-position="top">
<FormItem label="Title">
<Input v-model="formTop.input1"></Input>
</FormItem>
<FormItem label="Title name">
<Input v-model="formTop.input2"></Input>
</FormItem>
<FormItem label="Aligned title">
<Input v-model="formTop.input3"></Input>
</FormItem>
</Form>
</template>
<script>
export default {
data () {
return {
formLeft: {
input1: '',
input2: '',
input3: ''
},
formRight: {
input1: '',
input2: '',
input3: ''
},
formTop: {
input1: '',
input2: '',
input3: ''
}
}
}
}
</script>
Form 组件基于 async-validator 实现的数据验证,给 Form 设置属性 rules
,同时给需要验证的 FormItem 设置属性 prop
指向对应字段即可。
完整的验证规则请参照开源项目 async-validator。
验证方法也支持 Promise。
<template>
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="80">
<FormItem label="Name" prop="name">
<Input v-model="formValidate.name" placeholder="Enter your name"></Input>
</FormItem>
<FormItem label="E-mail" prop="mail">
<Input v-model="formValidate.mail" placeholder="Enter your e-mail"></Input>
</FormItem>
<FormItem label="City" prop="city">
<Select v-model="formValidate.city" placeholder="Select your city">
<Option value="beijing">New York</Option>
<Option value="shanghai">London</Option>
<Option value="shenzhen">Sydney</Option>
</Select>
</FormItem>
<FormItem label="Date">
<Row>
<Col span="11">
<FormItem prop="date">
<DatePicker type="date" placeholder="Select date" v-model="formValidate.date"></DatePicker>
</FormItem>
</Col>
<Col span="2" style="text-align: center">-</Col>
<Col span="11">
<FormItem prop="time">
<TimePicker type="time" placeholder="Select time" v-model="formValidate.time"></TimePicker>
</FormItem>
</Col>
</Row>
</FormItem>
<FormItem label="Gender" prop="gender">
<RadioGroup v-model="formValidate.gender">
<Radio label="male">Male</Radio>
<Radio label="female">Female</Radio>
</RadioGroup>
</FormItem>
<FormItem label="Hobby" prop="interest">
<CheckboxGroup v-model="formValidate.interest">
<Checkbox label="Eat"></Checkbox>
<Checkbox label="Sleep"></Checkbox>
<Checkbox label="Run"></Checkbox>
<Checkbox label="Movie"></Checkbox>
</CheckboxGroup>
</FormItem>
<FormItem label="Desc" prop="desc">
<Input v-model="formValidate.desc" type="textarea" :autosize="{minRows: 2,maxRows: 5}" placeholder="Enter something..."></Input>
</FormItem>
<FormItem>
<Button type="primary" @click="handleSubmit('formValidate')">Submit</Button>
<Button @click="handleReset('formValidate')" style="margin-left: 8px">Reset</Button>
</FormItem>
</Form>
</template>
<script>
export default {
data () {
return {
formValidate: {
name: '',
mail: '',
city: '',
gender: '',
interest: [],
date: '',
time: '',
desc: ''
},
ruleValidate: {
name: [
{ required: true, message: 'The name cannot be empty', trigger: 'blur' }
],
mail: [
{ required: true, message: 'Mailbox cannot be empty', trigger: 'blur' },
{ type: 'email', message: 'Incorrect email format', trigger: 'blur' }
],
city: [
{ required: true, message: 'Please select the city', trigger: 'change' }
],
gender: [
{ required: true, message: 'Please select gender', trigger: 'change' }
],
interest: [
{ required: true, type: 'array', min: 1, message: 'Choose at least one hobby', trigger: 'change' },
{ type: 'array', max: 2, message: 'Choose two hobbies at best', trigger: 'change' }
],
date: [
{ required: true, type: 'date', message: 'Please select the date', trigger: 'change' }
],
time: [
{ required: true, type: 'string', message: 'Please select time', trigger: 'change' }
],
desc: [
{ required: true, message: 'Please enter a personal introduction', trigger: 'blur' },
{ type: 'string', min: 20, message: 'Introduce no less than 20 words', trigger: 'blur' }
]
}
}
},
methods: {
handleSubmit (name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.$Message.success('Success!');
} else {
this.$Message.error('Fail!');
}
})
},
handleReset (name) {
this.$refs[name].resetFields();
}
}
}
</script>
可以完全自定义验证规则来完成更复杂的验证,比如某些数据需要在服务端验证时。示例展示的是密码的二次确认及模拟的一个异步验证。
<template>
<Form ref="formCustom" :model="formCustom" :rules="ruleCustom" :label-width="80">
<FormItem label="Password" prop="passwd">
<Input type="password" v-model="formCustom.passwd"></Input>
</FormItem>
<FormItem label="Confirm" prop="passwdCheck">
<Input type="password" v-model="formCustom.passwdCheck"></Input>
</FormItem>
<FormItem label="Age" prop="age">
<Input type="text" v-model="formCustom.age" number></Input>
</FormItem>
<FormItem>
<Button type="primary" @click="handleSubmit('formCustom')">Submit</Button>
<Button @click="handleReset('formCustom')" style="margin-left: 8px">Reset</Button>
</FormItem>
</Form>
</template>
<script>
export default {
data () {
const validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('Please enter your password'));
} else {
if (this.formCustom.passwdCheck !== '') {
// 对第二个密码框单独验证
this.$refs.formCustom.validateField('passwdCheck');
}
callback();
}
};
const validatePassCheck = (rule, value, callback) => {
if (value === '') {
callback(new Error('Please enter your password again'));
} else if (value !== this.formCustom.passwd) {
callback(new Error('The two input passwords do not match!'));
} else {
callback();
}
};
const validateAge = (rule, value, callback) => {
if (!value) {
return callback(new Error('Age cannot be empty'));
}
// 模拟异步验证效果
setTimeout(() => {
if (!Number.isInteger(value)) {
callback(new Error('Please enter a numeric value'));
} else {
if (value < 18) {
callback(new Error('Must be over 18 years of age'));
} else {
callback();
}
}
}, 1000);
};
return {
formCustom: {
passwd: '',
passwdCheck: '',
age: ''
},
ruleCustom: {
passwd: [
{ validator: validatePass, trigger: 'blur' }
],
passwdCheck: [
{ validator: validatePassCheck, trigger: 'blur' }
],
age: [
{ validator: validateAge, trigger: 'blur' }
]
}
}
},
methods: {
handleSubmit (name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.$Message.success('Success!');
} else {
this.$Message.error('Fail!');
}
})
},
handleReset (name) {
this.$refs[name].resetFields();
}
}
}
</script>
当需要动态维护 FormItem 时,也可以直接给 FormItem 设置属性 rules
来单独为该域做验证。
动态设置 FormItem 的 prop 属性时,会依据上层的 Form 组件的 model 来获取,查看示例代码。
FormItem 还可以独立设置 required、error 等属性,详见 API。
<template>
<Form ref="formDynamic" :model="formDynamic" :label-width="80" style="width: 300px">
<FormItem
v-for="(item, index) in formDynamic.items"
v-if="item.status"
:key="index"
:label="'Item ' + item.index"
:prop="'items.' + index + '.value'"
:rules="{required: true, message: 'Item ' + item.index +' can not be empty', trigger: 'blur'}">
<Row>
<Col span="18">
<Input type="text" v-model="item.value" placeholder="Enter something..."></Input>
</Col>
<Col span="4" offset="1">
<Button @click="handleRemove(index)">Delete</Button>
</Col>
</Row>
</FormItem>
<FormItem>
<Row>
<Col span="12">
<Button type="dashed" long @click="handleAdd" icon="md-add">Add item</Button>
</Col>
</Row>
</FormItem>
<FormItem>
<Button type="primary" @click="handleSubmit('formDynamic')">Submit</Button>
<Button @click="handleReset('formDynamic')" style="margin-left: 8px">Reset</Button>
</FormItem>
</Form>
</template>
<script>
export default {
data () {
return {
index: 1,
formDynamic: {
items: [
{
value: '',
index: 1,
status: 1
}
]
}
}
},
methods: {
handleSubmit (name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.$Message.success('Success!');
} else {
this.$Message.error('Fail!');
}
})
},
handleReset (name) {
this.$refs[name].resetFields();
},
handleAdd () {
this.index++;
this.formDynamic.items.push({
value: '',
index: this.index,
status: 1
});
},
handleRemove (index) {
this.formDynamic.items[index].status = 0;
}
}
}
</script>
API
Form props
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
model | 表单数据对象 | Object | - |
rules | 表单验证规则,具体配置查看 async-validator | Object | - |
inline | 是否开启行内表单模式 | Boolean | false |
label-position | 表单域标签的位置,可选值为 left 、right 、top | String | right |
label-width | 表单域标签的宽度,所有的 FormItem 都会继承 Form 组件的 label-width 的值 | Number | - |
show-message | 是否显示校验错误信息 | Boolean | true |
autocomplete | 原生的 autocomplete 属性,可选值为 off 或 on | String | off |
Form methods
方法名 | 说明 | 参数 |
---|---|---|
validate | 对整个表单进行校验,参数为检验完的回调,会返回一个 Boolean 表示成功与失败,支持 Promise | callback |
validateField | 对部分表单字段进行校验的方法,参数1为需校验的 prop,参数2为检验完回调,返回错误信息 | callback |
resetFields | 对整个表单进行重置,将所有字段值重置为空并移除校验结果 | 无 |
FormItem props
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
prop | 对应表单域 model 里的字段 | String | - |
label | 标签文本 | String | - |
label-width | 表单域标签的的宽度 | Number | - |
label-for | 指定原生的 label 标签的 for 属性,配合控件的 element-id 属性,可以点击 label 时聚焦控件。 | String | - |
required | 是否必填,如不设置,则会根据校验规则自动生成 | Boolean | - |
rules | 表单验证规则 | Object | Array | - |
error | 表单域验证错误信息, 设置该值会使表单验证状态变为error,并显示该错误信息 | String | - |
show-message | 是否显示校验错误信息 | Boolean | true |
FormItem slot
名称 | 说明 |
---|---|
无 | 内容 |
label | label 内容 |