自定义ElementUI中form表单组件
如何去自定义组件呢?首先根据如何去使用组件来构建组件,然后去实现每一个组件的细节。这里我们构建一个自定义
input
组件,实现双向数据绑定。
项目目录结构
1、环境使用vuecli3.0构建的项目
2、实现思路
2-1:首先通过使用elementUI的表单组件的结构,构建自定义表单组件结构。
2-2:从最里层开始实现,首先实现input
自定义组件。由于k-input
使用了v-model
,在k-input
组件中,需实现双向数据绑定,绑定value
,监听input
事件并分发。v-model
的实现原理就是绑定value
和监听input
事件。
2-3:表单校验需要安装一个校验库npm i async-validator
Index.vue
<template>
<div>
<h3>Element表单</h3>
<!-- model rules 需要放到form组件上,原因是内层的form-item都需要使用 -->
<k-form :model="model" :rules="rules" ref="loginForm">
<k-form-item label="用户名" prop="username">
<k-input v-model="model.username" autocomplete="off" placeholder="请输入用户名"></k-input>
</k-form-item>
<k-form-item label="密码" prop="password">
<k-input v-model="model.password" type="password" autocomplete="off" placeholder="请输入密码"></k-input>
</k-form-item>
<k-form-item>
<button @click="submitForm('loginForm')">提交</button>
</k-form-item>
</k-form>
</div>
</template>
<script>
import KForm from "./KForm";
import KFormItem from "./KFormItem";
import KInput from "./KInput";
export default {
components: {
KForm,
KFormItem,
KInput
},
data() {
return {
model: {username:'',password:''},
rules:{
username:[{required:true,message:'请输入用户名'}],
password:[{required:true,message:'请输入密码'}],
}
}
},
methods: {
submitForm(form) {
this.$refs[form].validate(valid => {
if (valid) {
alert("请求登录");
} else {
alert("校验失败");
}
});
}
}
};
</script>
<style lang="scss" scoped></style>
KInput.vue
<template>
<div>
<!-- 将k-input组件上的属性展开放到input组件是 -->
<input v-bind="$attrs" :value="value" @input="inputHandle">
</div>
</template>
<script>
export default {
// 取消默认将属性放在div上
inheritAttrs:false,
props:{
value:{
type:String,
default:''
}
},
methods: {
inputHandle(e) {
// 分发k-input的input的事件,实现双向数据绑定
this.$emit('input',e.target.value)
// 分发校验事件 每次输入框输入了数据就就行校验
this.$parent.$emit('validate')
}
},
}
</script>
<style lang="scss" scoped>
</style>
KFormItem.vue
<template>
<div>
<label v-if="label">{{ label }}</label>
<slot></slot>
<p v-if="errorMsg">{{ errorMsg }}</p>
</div>
</template>
<script>
import Schema from 'async-validator'
export default {
inject:['form'],
props: {
label: {
type: String,
default: ''
},
prop:{
type:String
}
},
data(){
return {
errorMsg:''
}
},
mounted(){
this.$on('validate',this.validate)
},
methods: {
validate() {
const value = this.form.model[this.prop]
const rules = this.form.rules[this.prop]
// 校验的描述对象
const desc = {[this.prop]:rules}
const schema = new Schema(desc)
// 校验 return返回的是promise对象
return schema.validate({[this.prop]:value},errors=>{
if(errors){
this.errorMsg = errors[0].message
}else{
this.errorMsg = ''
}
})
}
},
}
</script>
<style lang="scss" scoped>
</style>
KForm.vue
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
provide() {
return {
form: this
};
},
props: {
model: {
type: Object,
required: true
},
rules: {
type: Object
}
},
methods: {
validate(cb) {
const tasks = this.$children
.filter(item => item.prop)
.map(item => item.validate());
Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false));
}
}
};
</script>
<style lang="scss" scoped></style>
自定义信息提示框组件
这类组件的特点在于,它们是独立于当前
Vue
实例之外独立存在的,通常挂载在body
上,是通过JavaScript动态创建的,不需要在任何组件中声明。
目录结构
KNotice.vue
<template>
<div class="show-box" v-if="isShow">
<h3>{{ title }}</h3>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
isShow: false
}
},
props: {
message: {
type: String
},
title: {
type: String
},
duration: {
type: Number,
default:1000
}
},
methods: {
show() {
this.isShow = true
setTimeout(this.hide, this.duration);
},
hide(){
this.isShow = false
this.remove()
}
},
};
</script>
<style lang="scss" scoped>
.show-box{
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
margin: auto;
width: 200px;
height: 200px;
}
</style>
Index.vue
<template>
<div>
<button @click="ShowHandle">显示</button>
</div>
</template>
<script>
import create from "../../utils/create";
import Notice from "./KNotice";
export default {
methods: {
ShowHandle() {
const notice = create(Notice, {
title: "校验成功",
message: "你好牛逼哦",
duration: 2000
});
notice.show()
}
}
};
</script>
<style lang="scss" scoped></style>
create.js
import Vue from "vue";
export default function create(Component, props) {
// 创建Vue实例 先创建实例不挂载 直接将虚拟DOM挂载body上 会报错的
const vm = new Vue({
// 创建虚拟DOM 入参:createElement简写为h
render(h) {
// h函数返回的是虚拟DOM
return h(Component, { props });
}
}).$mount();
// 手动挂载 vm.$el是真实DOM
document.body.appendChild(vm.$el)
// 销毁
const comp = vm.$children[0]
comp.remove = function(){
// 移除DOM
document.body.removeChild(vm.$el)
// 销毁组件
vm.$destroy()
}
return comp
}
自定义树形组件
目录结构
Item.vue
<template>
<li>
<div @click="toggle">
{{ model.title }}
<span v-if="isFolder">[{{ open ? "-" : "+" }}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="model in model.children"
:model="model"
:key="model.id"
></item>
</ul>
</li>
</template>
<script>
export default {
name: "Item",
props: {
model: {
type: Object
}
},
data() {
return {
open: false
};
},
computed: {
isFolder() {
return this.model.children && this.model.children.length;
}
},
methods: {
toggle() {
if (this.isFolder) {
this.open = !this.open;
}
}
}
};
</script>
<style lang="scss" scoped></style>
index.vue
<template>
<div>
<ul>
<item :model="treeData"></item>
</ul>
</div>
</template>
<script>
import Item from "./Item";
export default {
components: {
Item
},
data() {
return {
treeData: {
title: "web全栈架构师",
children: [
{
title: "java工程师",
children: [
{
title: "python工程师"
}
]
},
{
title: "go工程师",
children: [
{
title: "C#工程师"
}
]
}
]
}
};
}
};
</script>
<style lang="scss" scoped></style>