一、Element Form
资料地址:https://element.eleme.cn/#/zh-CN/component/form
下面以Form表单为例,介绍Element UI的使用。
第1步:使用脚手架创建vue工程;
vue create vue-form
第2步:添加element插件。
vue add element
选择按需加载:
第3步:在App.js文件中定义Form表单;
<template>
<div id="app">
<h3>{{title}}</h3>
<el-form :model="ruleForm" :rules="rules" ref="loginForm">
<el-form-item label="用户名" prop="name">
<el-input v-model="ruleForm.name" placeholder="用户名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pwd">
<el-input v-model="ruleForm.pwd" placeholder="密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
ruleForm: {
name: '',
pwd: '',
},
rules: {
name: [
{required: true, message: '请输入用户名'},
],
pwd: [
{required: true, message: '请输入密码'},
{min: 6, max: 10, message: '请输入6~10位的密码'},
]
}
}
},
methods: {
submitForm() {
}
},
}
</script>
<style scoped>
</style>
ruleForm属性用户绑定输入框,如:ruleForm.name绑定用户名输入框,ruleForm.pwd绑定密码输入框。rules属性定义了各个表单项的校验规则。
第4步:修改plugins/element.js文件,导入Button、Form、FormItem、Input组件;
import Vue from 'vue'
import { Button, Form, FormItem, Input } from 'element-ui'
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
二、自定义Form组件
2.1 组件设计
通过上面Form组件的使用,我们可以看出表单组件的结构如下图所示:
其中,表单各个组件的职责分为:
1)Form负责定义校验规则;
2)FormItem负责显示错误信息;
3)Input负责数据的双向绑定;
4)Button负责表单校验,以及提交表单;
2.2 构建表单
下面程序模拟Element UI表单构建出我们的表单结构:
<template>
<k-form :model="ruleForm" :rules="rules" ref="loginForm">
<k-form-item label="用户名" prop="name">
<k-input v-model="ruleForm.name"></k-input>
</k-form-item>
<k-form-item label="密码" prop="pwd">
<k-input v-model="ruleForm.pwd" type="password"></k-input>
</k-form-item>
<k-form-item>
<el-button type="primary" @click="submitForm">登录</el-button>
</k-form-item>
</k-form>
{{ruleForm}}
</template>
<script>
import KForm from "./Form.vue";
import KFormItem from "./FormItem.vue";
import KInput from "./Input.vue";
export default {
components: {
KForm,
KFormItem,
KInput,
},
data() {
return {
ruleForm: {
name: '',
pwd: '',
},
rules: {
name: [
{required: true, message: '请输入用户名'},
],
pwd: [
{required: true, message: '请输入密码'},
{min: 6, max: 10, message: '请输入6~10位的密码'},
]
}
}
},
methods: {
submitForm() {
this.$refs.loginForm.validate(valid => {
if (valid) {
alert('提交登录');
} else {
console.log('校验失败');
return false;
}
});
}
}
}
</script>
<style scoped>
</style>
2.3 定义Form
第1步:在components目录下新建Form.vue文件;
第2步:定义模版;
<template>
<form>
<slot></slot>
</form>
</template>
第3步:定义属性。
<script>
export default {
provide() {
return {
form: this // 将表单实例传输给后代
}
},
props: {
model: {
type: Object,
required: true,
},
rules: {
type: Object
}
},
}
</script>
2.4 定义FormItem
第1步:在components目录下新建FormItem.vue文件;
第2步:定义模版;
<template>
<div>
<label v-if="label">{{label}}</label>
<div>
<!-- 定义插槽 -->
<slot></slot>
<!-- 校验结果 -->
<p v-if="validateStatus == 'error'" class="error">{{errorMessage}}</p>
</div>
</div>
</template>
第3步:定义属性;
<script>
export default {
inject: ['form'], // 注入form,获取model和rules属性
props: ['label', 'prop'],
data() {
return {
validateStatus: '',
errorMessage: '',
}
},
}
</script>
2.5 定义Input
第1步:在components目录下新建Input.vue文件;
第2步:定义模版;
<template>
<div>
<!-- 用户输入数据时会自动触发input事件 -->
<input :type="type" :value="inputValue" @input="handleInput"/>
</div>
</template>
第3步:定义属性;
<script>
export default {
props: {
value: {
type: String,
defaultValue: '',
},
type: {
type: String,
defaultValue: 'text',
},
},
data() {
return {
inputValue: this.value
}
},
methods: {
// 当输入框内容发生变化,会自动触发@input事件,从而执行handlerInput函数
handleInput(e) {
// e.target.value返回输入框的内容
this.inputValue = e.target.value;
// 通知父组件更新
this.$emit('input', this.inputValue);
}
}
}
</script>
2.5 添加表单项校验
首先这里再强调一下,表单项校验应该有FileItem完成。
第1步:修改handleInput方法,向FormItem组件派发一个validate事件;
methods: {
handleInput(e) {
...
// 通知父组件FormItem做校验
this.$parent.$emit('validate', this.inputValue);
}
}
第2步:在FormItem的created生命周期方法中监听validate事件;
created() {
this.$on('validate', this.validate);
},
第3步:在FormItem中定义validate函数。
methods: {
// 使用async-validator进行校验
validate(value) {
// 该Promise封装了校验代码
// 然后在父组件中使用Promise.all()函数保证组件校验的执行顺序
return new Promise((resolve) => {
// 校验规则
const descriptor = {
[this.prop]: this.form.rules[this.prop]
};
// 校验器
const validator = new schema(descriptor);
// 调用校验方法validate
// 第一个参数:需要校验的数据模型
// 第二个参数:回调函数
validator.validate({[this.prop]: value}, (errors) => {=> {
if (errors) {
// 校验失败
this.validateStatus = 'error';
this.errorMessage = errors[0].message;
resolve(false);
} else {
// 校验成功
this.validateStatus = '';
this.errorMessage = '';
resolve(true);
}
});
});
},
}
为了保证多个表单项校验的执行顺序,上面把校验结果添加到Promise对象中。如果校验成功,返回true,否则返回false。
第4步:在FormItem中导入async-validator。
<script>
import schema from 'async-validator';
export default {
...
}
</script>
FormItem文件的完成代码如下所示:
<template>
<div>
<label v-if="label">{{label}}</label>
<div>
<slot></slot>
<p v-if="validateStatus == 'error'" class="error">{{errorMessage}}</p>
</div>
</div>
</template>
<script>
import schema from 'async-validator';
export default {
inject: ['form'], // 注入form,获取model和rules属性
props: ['label', 'prop'],
data() {
return {
validateStatus: '',
errorMessage: '',
}
},
created() {
this.$on('validate', this.validate);
},
methods: {
// 使用async-validator进行校验
validate(value) {
// 该Promise封装了校验代码
// 然后在父组件中使用Promise.all()函数保证组件校验的执行顺序
return new Promise((resolve) => {
// 校验规则
const descriptor = {
[this.prop]: this.form.rules[this.prop]
};
// 校验器
const validator = new schema(descriptor);
// 调用校验方法validate
// 第一个参数:需要校验的数据模型
// 第二个参数:回调函数
validator.validate({[this.prop]: value}, (errors) => {
if (errors) {
// 校验失败
this.validateStatus = 'error';
this.errorMessage = errors[0].message;
resolve(false);
} else {
// 校验成功
this.validateStatus = '';
this.errorMessage = '';
resolve(true);
}
});
});
},
}
}
</script>
<style scoped>
.error {
color: red;
}
</style>
2.6 添加表单校验
第1步:在FormItem组件的mounted函数中,向Form组件派发一个formItemAdd事件,并把当前需要校验的FormItem组件发送给Form组件;
mounted() { // 当前组件挂载后,派发一个事件
// 只有当FormItem有prop属性时才需要派发事件
if (this.prop) {
// 向Form组件派发事件,然后把当前组件发送给Form组件
this.$parent.$emit('formItemAdd', this);
}
},
第2步:在Form组件的created函数中,监听formItemAdd事件,并且把校验的FormItem存储到fields中;
created() {
// 监听事件,当FormItem组件挂载后,缓存需要校验的FormItem组件
this.fields = [];
this.$on('formItemAdd', (item) => this.fields.push(item));
},
第3步:在Form组件中定义校验方法;
methods: {
async validate(callback) {
// 提交表单时候,重新校验所有表单项的validate方法
// 然后等待所有校验结果返回后,再进行统一处理
const promises = this.fields.map(item => item.validate());
const results = await Promise.all(promises);
let ret = true; // 是否校验成功,默认成功
results.forEach(valid => {
if (!valid) {
ret = false; // 只要有一个表单项校验失败,则校验失败
}
});
callback(ret);
}
}
第4步:修改FormItem的validate方法,把value参数去掉,然后从form组件的model属性中动态获取value。
this.form.model[this.prop]}
第5步:修改登录按钮的事件函数,执行表单验证。
methods: {
submitForm() {
this.$refs.loginForm.validate(valid => {
if (valid) {
alert('提交登录');
} else {
console.log('校验失败');
return false;
}
});
}
}