本文目标
- 学会使用elementUI的表单组件
- 组件的传值方式
- 封装input组件、带校验的form组件
先看看elementUI的表单组件是怎么用的
1、 el-form
:接收两个参数:model
接收外部传的表单的数据,rules
接收校验规则
2、el-form-item
“这个组件主要展示label和显示校验的错误信息
3、el-input
:双向绑定
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="活动名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
</el-form>
封装input组件
1、自定义组件要使用v-model
,则必须要:value / @input
2、触发input事件的时候,同时派发校验事件,告诉父组件去校验
3、这里用到了v-bind="$attrs"
: 将调用组件时的组件标签上绑定的非props的特性(class和style除外)向下传递。在子组件中应当添加inheritAttrs: false
(避免父作用域的不被认作props的特性绑定应用在子组件的根元素上)。注意: $attrs
存储的是props之外的部分
4、与attrs类似的还有:v-on="$listeners"
:将父组件标签上的自定义事件向下传递其子组件可以直接通过emit(eventName)的方式调用
<template>
<div class="m-input">
<input :value="value" @input="onInput" v-bind="$attrs">
</div>
</template>
<script>
export default {
inheritAttrs: false, // 避免顶层容器继承属性
props: {
value: {
type: String,
default: ''
}
// 因为用了$attrs 所以placeholder不需要这个props就不需要写了
},
methods: {
onInput(e) {
// 通知父组件数值变化
this.$emit('input', e.target.value);
// 通知FormItem校验
this.$parent.$emit('validate');
}
},
}
</script>
封装form-item组件
- 展示label和校验错误的信息
- 留插槽,里面放input
- 对单个项进行校验
<template>
<div class="m-form-item">
<i class="warn-icon" v-if="isNecessary&&label">*</i>
<label v-if="label">
<span>{{label}}</span>
</label>
<slot></slot>
<p class="warning-msg">{{warningMsg}}</p>
</div>
</template>
这里的校验需要用到一个校验库async-validator
,具体用法见 github地址
// 安装
npm i async-validator
// 用法
import Schema from 'async-validator';
const descriptor = {
name(rule, value, callback, source, options) {
const errors = [];
if (!/^[a-z0-9]+$/.test(value)) {
errors.push(new Error(
util.format('%s must be lowercase alphanumeric characters', rule.field),
));
}
return errors;
},
};
const validator = new Schema(descriptor);
validator.validate({ name: 'Firstname' }, (errors, fields) => {
if (errors) {
return handleErrors(errors, fields);
}
// validation passed
});
js部分要做的是接受当前项的数据,以及当前项的校验规则,最后进行校验。但是form-item
接收的参数只有两个:label
和prop
都是字符串,拿不到form表单的数据。
由于可以嵌套多层表单,因此不能只用父子传值,把数据传进来。这时候就涉及vue组件传值、通信的几种方式;
组件传值、通信
父=>子
- 属性props
- 引用refs:
this.$refs.form.mobile='123'
- children:
this.$children[0].mobile='123'
子=>父
- 自定义事件:
$emit('name',123)
/@name
兄弟通信
通过同一个父亲组件搭桥
this.$parent.on('name',123)
/this.$parent.emit('name')
爷孙之间
祖父辈通过provide
提供数据
provide() {
return {formData: 123}
}
子孙辈通过inject
注入数据
inject['formData']
任意两个组件
回到form-item
获取到最外层父组件form
的数据,因此需要form
组件provide
当前实例给孙子组件,那么form-item
就可以注入整个form
表单的实例
import Schema from "async-validator"
data() {
return {
isNecessary: false, // 是否必填
warningMsg: '' // 错误的校验信息
}
},
inject: ['from'], // form表单提供的数据
props: {
label: String,
prop: String
}
数据以及规则都已经拿到,下面就是进行校验了!下面按照async-validator
文档的用法写校验方法
methods: {
onValidate() {
console.log(this.prop) //当前item的规则名称
console.log(this.form) //整个form的实例:{model:外部接收的数据, rules:外部接收的规则}
const descriptor = { // 这里拿到规则描述
[this.prop]: this.from.rules[this.prop]
}
const validator = new Schema(descriptor);
return validator.validate({ [this.prop]: this.from.model[this.prop] }, (errors) => {
if (errors) {
console.log(errors);
this.warningMsg = errors[0].message
} else {
// 校验通过则清除错误消息
this.warningMsg = ''
}
})
}
}
最后监听校验事件,并且执行校验方法
mounted() {
if (JSON.stringify(this.from.rules) !== '{}') {
this.isNecessary = true
}
this.$on('validate', () => {
this.onValidate()
})
},
封装form组件
<template>
<div>
<slot></slot>
</div>
</template>
js部分就是要处理两件事:
- 接收外部数据和校验规则数组
- 对规则进行全局校验
provide() {
return {
'from': this // 直接传整个实例过去
}
},
props: {
model: {
type: Object,
required: true
},
rules: Object
}
下面是拿到全部form-item
的校验方法return出来的结果;注意: async-validator
return的校验结果是一个Promise。
这里只需要拿出全部的校验任务,并且执行就可以
methods: {
validate(cb) {
const tasks = this.$children
.filter(item => item.prop)
.map(item => item.onValidate());
// 所有任务必须全部成功才算校验通过
console.log(tasks); // tasks是一个promise的数组
// Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值
Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false));
}
}
这样一个基本的表单就完成,用法也与elementUI基本一致
当提交表单的时候只需要通过this.$refs
调用form
的validate()
方法,就可以对整个表单校验
<template>
<div class="home">
<m-form :model="formData" :rules="rules" ref="form">
<m-form-item label="手机号" prop="phone">
<m-input type="text" v-model="formData.phone" placeholder="输入手机号" />
</m-form-item>
<m-form-item label="密码" prop="password">
<m-input type="password" v-model="formData.password" />
</m-form-item>
<m-form-item>
<div @click="submit">提交</div>
</m-form-item>
</m-form>
</div>
</template>
<script>
import mForm from '@/components/mForm/index.vue'
import mFormItem from '@/components/mForm/Item.vue'
import mInput from '@/components/mInput/index.vue'
export default {
name: 'Home',
components: {
mForm,
mFormItem,
mInput
},
data() {
return {
formData: {
phone: '',
password: ''
},
rules: {
phone: [
{ required: true, message: '请输入手机号' }
],
password: [
{ required: true, message: '请输入密码' }
]
}
}
},
methods: {
submit() {
this.$refs.form.validate((valid) => {
console.log(valid);
if (valid) {
alert('submit!');
} else {
alert('error!');
return false;
}
});
}
},
}
</script>
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!