自定義ElementUI中form表單組件
如何去自定義組件呢?首先根據(jù)如何去使用組件來構(gòu)建組件掘宪,然后去實(shí)現(xiàn)每一個(gè)組件的細(xì)節(jié)寇钉。這里我們構(gòu)建一個(gè)自定義
input
組件,實(shí)現(xiàn)雙向數(shù)據(jù)綁定违寿。
項(xiàng)目目錄結(jié)構(gòu)
1湃交、環(huán)境使用vuecli3.0構(gòu)建的項(xiàng)目
2、實(shí)現(xiàn)思路
? ?2-1:首先通過使用elementUI的表單組件的結(jié)構(gòu)藤巢,構(gòu)建自定義表單組件結(jié)構(gòu)搞莺。
? ?2-2:從最里層開始實(shí)現(xiàn),首先實(shí)現(xiàn)input
自定義組件掂咒。由于k-input
使用了v-model
才沧,在k-input
組件中迈喉,需實(shí)現(xiàn)雙向數(shù)據(jù)綁定,綁定value
温圆,監(jiān)聽input
事件并分發(fā)挨摸。v-model
的實(shí)現(xiàn)原理就是綁定value
和監(jiān)聽input
事件。
? ?2-3:表單校驗(yàn)需要安裝一個(gè)校驗(yàn)庫npm i async-validator
image.png
Index.vue
<template>
<div>
<h3>Element表單</h3>
<!-- model rules 需要放到form組件上捌木,原因是內(nèi)層的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="請(qǐng)輸入用戶名"></k-input>
</k-form-item>
<k-form-item label="密碼" prop="password">
<k-input v-model="model.password" type="password" autocomplete="off" placeholder="請(qǐng)輸入密碼"></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:'請(qǐng)輸入用戶名'}],
password:[{required:true,message:'請(qǐng)輸入密碼'}],
}
}
},
methods: {
submitForm(form) {
this.$refs[form].validate(valid => {
if (valid) {
alert("請(qǐng)求登錄");
} else {
alert("校驗(yàn)失敗");
}
});
}
}
};
</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 {
// 取消默認(rèn)將屬性放在div上
inheritAttrs:false,
props:{
value:{
type:String,
default:''
}
},
methods: {
inputHandle(e) {
// 分發(fā)k-input的input的事件油坝,實(shí)現(xiàn)雙向數(shù)據(jù)綁定
this.$emit('input',e.target.value)
// 分發(fā)校驗(yàn)事件 每次輸入框輸入了數(shù)據(jù)就就行校驗(yàn)
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]
// 校驗(yàn)的描述對(duì)象
const desc = {[this.prop]:rules}
const schema = new Schema(desc)
// 校驗(yàn) return返回的是promise對(duì)象
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>
自定義信息提示框組件
這類組件的特點(diǎn)在于,它們是獨(dú)立于當(dāng)前
Vue
實(shí)例之外獨(dú)立存在的刨裆,通常掛載在body
上澈圈,是通過JavaScript動(dòng)態(tài)創(chuàng)建的,不需要在任何組件中聲明帆啃。
目錄結(jié)構(gòu)
image.png
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: "校驗(yàn)成功",
message: "你好牛逼哦",
duration: 2000
});
notice.show()
}
}
};
</script>
<style lang="scss" scoped></style>
create.js
import Vue from "vue";
export default function create(Component, props) {
// 創(chuàng)建Vue實(shí)例 先創(chuàng)建實(shí)例不掛載 直接將虛擬DOM掛載body上 會(huì)報(bào)錯(cuò)的
const vm = new Vue({
// 創(chuàng)建虛擬DOM 入?yún)ⅲ篶reateElement簡(jiǎn)寫為h
render(h) {
// h函數(shù)返回的是虛擬DOM
return h(Component, { props });
}
}).$mount();
// 手動(dòng)掛載 vm.$el是真實(shí)DOM
document.body.appendChild(vm.$el)
// 銷毀
const comp = vm.$children[0]
comp.remove = function(){
// 移除DOM
document.body.removeChild(vm.$el)
// 銷毀組件
vm.$destroy()
}
return comp
}
自定義樹形組件
目錄結(jié)構(gòu)
image.png
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全棧架構(gòu)師",
children: [
{
title: "java工程師",
children: [
{
title: "python工程師"
}
]
},
{
title: "go工程師",
children: [
{
title: "C#工程師"
}
]
}
]
}
};
}
};
</script>
<style lang="scss" scoped></style>
效果圖
image.png