自定义组件实现v-model
v-model的本质:
<input v-model="variable" />
// 等价于
<input :value="variable" @input="variable = $event.target.value" />
相信大家都会倾向于第一种写法,这个是封装好的组件,如果是我们自己写的组件,v-model有没有效果呢?很明显,直接写是没有效果的,不下点功夫怎么会有回报,我们用写好的组件方便是因为有些事别人替我们做了。要想方便以后使用,那现在就要多下功夫,不但要知其然,还要知其所以然。
父组件中
<hTimePicker v-model='dataPickerShow' :sTime="9" :dayNum='7' :sDay='0' :cTime="20" interval="5" @changeTime="changeTime"></hTimePicker>
dataPickerShow是我们要双向绑定的变量,changeTime是子组件向父组件传参的方法名,这里父组件用来监听子组件的事件,并获取传递过来的参数。其它参数是父组件传递给子组件的参数。这里可以看到,我们的父组件的写法是不是跟第二种形式很像,这样写就更明显了
<hTimePicker :show='dataPickerShow' :sTime="9" :dayNum='7' :sDay='0'
:cTime="20" interval="5" @changeStatus="changeStatus" @changeTime="changeTime">
</hTimePicker>
:show–:value @input=“variable = $event.target.value”–@changeStatus=“changeStatus”
关键在于如何让v-model生效
子组件中
model: {
prop: 'value',//参数名
event: 'input',//事件名
},
props: {
value: {
type: [Boolean],
default: false
},
}
这样,value参数的值就会同步父组件v-model的值,子组件传的参数改变的同时要触发event,就是类似@input="v-model的值 = e v e n t . t a r g e t . v a l u e " 的 效 果 , 我 们 这 里 可 以 用 event.target.value"的效果,我们这里可以用 event.target.value"的效果,我们这里可以用emit来触发,结合watch方法就是这样
watch:{
value(newVal,oldVal){
console.log('value',newVal,oldVal)
this.popShow=this.value
},
popShow(newVal,oldVal){
console.log('popShow',newVal,oldVal)
this.$emit('input', newVal);
}
},
这里我有引入了一个变量,它是子组件的,注意尽量不要为了省事用同一个变量,组件的数据流是单向的,默认不允许子组件修改父组件的值。这样,父组件传参导致子组件的参数变化,触发事件,父组件监听到该事件并将此作为v-model的值,形成一个闭环,就实现了双向绑定。
这里需要注意,我的参数名是value,事件是input,和input的一样,那可不可以自定义呢?目前我验证的h5中是可以的,小程序中必须是input value。
在父组件中通过监听input事件可以获取到子组件传的参数
父组件:
<hTimePicker v-model='dataPickerShow' :sTime="9" :dayNum='7' :sDay='0'
:cTime="20" interval="5" @changeTime="changeTime" @input="changeStatus">
</hTimePicker>
测试没有input不写@input="changeStatus"也可实现,不同平台可能会有差异。
全部代码如下:基于uview
<template>
<view>
<view class="changeTime" @click="dataPickerShow=true">
点击选择时间{{dataPickerShow}}
</view>
<hTimePicker v-model='dataPickerShow' :sTime="9" :dayNum='7' :sDay='0' :cTime="20" interval="5" @changeTime="changeTime" >
</hTimePicker>
</view>
</template>
<script>
import hTimePicker from "@/components/h-timePicker/h-timePicker.vue";
export default {
components: { hTimePicker },
data() {
return {
dataPickerShow:false
}
},
methods: {
changeStatus(status){
console.log(status)
this.dataPickerShow=status
},
changeTime(time,e){
console.log(time.slice(0,16),e)
// this.dataPickerShow=status
}
}
}
</script>
<style>
</style>
子组件:
<!-- v-model -->
<template>
<view>
<u-picker v-model="popShow" class="time-picker" mode="multiSelector" @confirm="bindStartMultiPickerChange" @columnchange="bindMultiPickerColumnChange" :default-selector="multiIndex" :range="multiArray">
</u-picker>
</view>
</template>
<script>
export default {
//小程序中事件只能用input
// model: {
// prop: 'value',
// event: 'input'
// },
model: {
prop: 'value',
event: 'input'
},
props: {
value: {
//开始小时
type: [Boolean],
default: false
},
sTime: {
//开始小时
type: [Number, String],
default: '0'
},
cTime: {
//结束小时
type: [Number, String],
default: '23'
},
timeNum: {
//延迟小时
type: [Number, String],
default: '1'
},
interval: {
//分钟间隔
type: [Number, String],
default: '1'
},
sDay: {
//开始天数
type: [Number, String],
default: '0'
},
dayNum: {
//预约天数
type: [Number, String],
default: '7'
}
},
data() {
return {
popShow:this.value,
sDayNum: 0,
multiArray: [['今天', '明天', '3-2', '3-3', '3-4', '3-5'], [0, 1, 2, 3, 4, 5, 6], [0, 10, 20]],
multiIndex: [0, 0, 0],
multiSelector: ''
};
},
beforeMount() {
this.pickerTap();
},
mounted() {
// this.popShow=this.value
console.log('组件调用',this.value)
},
watch:{
value(newVal,oldVal){
console.log('value',newVal,oldVal)
this.popShow=this.value
},
popShow(newVal,oldVal){
console.log('popShow',newVal,oldVal)
this.$emit('input', newVal);
}
},
methods: {
timeFormat: function(num) {
if (num < 10 && (num + '').length == 1) {
return '0' + num;
}
return num;
},
pickerTap: function() {
let date = new Date();
let monthDay = [];
let hours = [];
let minute = [];
this.sDayNum = this.sDay;
// 时
let date1 = new Date(date);
let sT = +this.sTime;
let eT = +this.cTime;
if (sT <= eT) {
let h = date1.getHours() < sT ? sT : date1.getHours();
h = h + parseInt(this.timeNum);
if (h > eT || this.sDayNum > 0) {
this.sDayNum = this.sDayNum <= 0 ? parseInt(this.sDay) + 1 : parseInt(this.sDay);
for (let i = sT; i <= eT; i++) {
hours.push(this.timeFormat(i) + '时');
}
} else {
for (let i = h; i <= eT; i++) {
hours.push(this.timeFormat(i) + '时');
}
}
} else {
let h = date1.getHours() < sT ? sT : date1.getHours();
h = h + parseInt(this.timeNum);
if ((h > eT && h < sT) || h > 23 || this.sDayNum > 0) {
this.sDayNum = this.sDayNum <= 0 ? parseInt(this.sDay) + 1 : parseInt(this.sDay);
for (let i = 0; i <= 23; i++) {
if (i < sT && i > eT) {
} else {
hours.push(this.timeFormat(i) + '时');
}
}
} else {
for (let i = h; i <= 23; i++) {
if (i < sT && i > eT) {
} else {
hours.push(this.timeFormat(i) + '时');
}
}
}
}
// 月-日
for (let i = +this.sDayNum; i <= parseInt(this.sDayNum) + parseInt(this.dayNum); i++) {
let date1 = new Date(date);
date1.setDate(date.getDate() + i);
let md = date1.getFullYear() + '-' + this.timeFormat(date1.getMonth() + 1) + '-' + this.timeFormat(date1.getDate());
monthDay.push(md);
}
let inter = +this.interval < 60 ? +this.interval : 59;
// 分
for (let i = 0; i < 60; i += inter) {
minute.push(i < 10 ? '0' + i + '分' : i + '分');
}
let data = {
multiArray: this.multiArray,
multiIndex: this.multiIndex
};
console.log(hours)
data.multiArray[0] = monthDay;
data.multiArray[1] = hours;
data.multiArray[2] = minute;
console.log(data.multiArray)
this.multiArray = data.multiArray;
this.multiIndex = data.multiIndex;
},
bindMultiPickerColumnChange(e) {
console.log(e)
this.multiIndex.splice(e.column,1,e.index)
let hours = [];
if (e.column == 0 && e.index == 0 && +this.sDayNum == 0) {
let date = new Date();
// 时
let date1 = new Date(date);
let sT = +this.sTime;
let eT = +this.cTime;
if (sT <= eT) {
let h = date1.getHours() < sT ? sT : date1.getHours();
h = h + parseInt(this.timeNum);
if (h > eT || this.sDayNum > 0) {
this.sDayNum = this.sDayNum <= 0 ? parseInt(this.sDay) + 1 : parseInt(this.sDay);
for (let i = sT; i <= eT; i++) {
hours.push(this.timeFormat(i) + '时');
}
} else {
for (let i = h; i <= eT; i++) {
hours.push(this.timeFormat(i) + '时');
}
}
} else {
let h = date1.getHours() < sT ? sT : date1.getHours();
h = h + parseInt(this.timeNum);
if ((h > eT && h < sT) || h > 23 || this.sDayNum > 0) {
this.sDayNum = this.sDayNum <= 0 ? parseInt(this.sDay) + 1 : parseInt(this.sDay);
for (let i = 0; i <= 23; i++) {
if (i < sT && i > eT) {
} else {
hours.push(this.timeFormat(i) + '时');
}
}
} else {
for (let i = h; i <= 23; i++) {
if (i < sT && i > eT) {
} else {
hours.push(this.timeFormat(i) + '时');
}
}
}
}
this.multiArray.splice(1,1,hours)
} else if (e.column == 0 && e.index != 0) {
let sT = +this.sTime;
let eT = +this.cTime;
if (sT <= eT) {
for (let i = sT; i <= eT; i++) {
hours.push(this.timeFormat(i) + '时');
}
} else {
for (let i = 0; i <= 23; i++) {
if (i < sT && i > eT) {
} else {
hours.push(this.timeFormat(i) + '时');
}
}
}
this.multiArray.splice(1,1,hours)
}
},
bindStartMultiPickerChange(e) {
console.log(e);
this.multiIndex = e;
let da = this.multiArray;
let di = e;
let caseDate = da[0][di[0]] + ' ' + da[1][di[1]].replace('时', ':') + this.timeFormat(da[2][di[2]].replace('分', '')) + ':00';
let appointTime = new Date(caseDate.replace(/-/g,'/')).getTime() / 1000;
if (appointTime < new Date().getTime() / 1000) {
uni.valueToast({
title: '不能选择过去时间',
icon: 'none'
});
return false;
}
this.$emit('changeTime', caseDate, appointTime * 1000);
}
}
};
</script>
<style lang="scss"></style>