一、Element Form

资料地址:https://element.eleme.cn/#/zh-CN/component/form

下面以Form表单为例,介绍Element UI的使用。

第1步:使用脚手架创建vue工程;

vue create vue-form

第2步:添加element插件。

vue add element

选择按需加载:

vue element画方形 vue element教程_vue

第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组件的使用,我们可以看出表单组件的结构如下图所示:

vue element画方形 vue element教程_前端_02


其中,表单各个组件的职责分为:

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;
            }
        });
    }
}