uView的DatetimePicker组件在confirm回调中取不到v-model的最新值

901 阅读2分钟

前情

uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE让开发体验非常棒,公司项目就是主推uni-app,在uniapp生态中uView是其中非常好的全平台的第三方开源ui库,我在公司项目中果断的使用了它。

我一个过滤弹窗有二个表单需要选择开始日期和结束日期,于是想到复用一个日期选择组件,通过设置不同标志来区分是不同表单触发的,再在confrim回调里根据不同标志把v-model上的值赋值到不同表单上

示例代码片段:

<template>
    <view class="page-container">
        <custom-popup 
            :isVisble="filterFormVisible" 
            custom-style="top:206rpx;" 
            @maskClick="fliterFormToggle"
        >
            <view class="filter-main">
                <view class="filter-content">
                    // ......
                    <text class="filter-type">入驻时间:</text>
                    <view class="filter-item-date-wrap">
                        <u-input 
                            v-model="filterDateStart" 
                            placeholder="选择开始时间" 
                            readonly 
                            @click.native="pickerDate('filterDateStart')"
                        >
                            <text slot="prefix" class="iconfont">&#xe65f;</text>
                        </u-input>
                        <view class="filter-connect"></view>
                        <u-input 
                            v-model="filterDateEnd" 
                            placeholder="选择结束时间" 
                            readonly 
                            @click.native="pickerDate('filterDateEnd')"
                        >
                            <text slot="prefix" class="iconfont">&#xe65f;</text>
                        </u-input>
                    </view>
                </view>
                // ......
            </view>
        </custom-popup>

        // 日期选择器
        <u-datetime-picker
            :show="showDatePicker"
            ref="datetimePicker"
            mode="date"
            v-model="filterDate"
            :formatter="formatter"
            @cancel="showDatePicker = false"
            @confirm="confirmFilterDate"
        />

    </view>
</template>

<script>
	import customPopup from '../../components/customPopup/customPopup.vue';
	
	export default {
            components: {
                customPopup
            },
            data() {
                return {
                    filterFormVisible: false,
                    showDatePicker: false,
                    filterDateStart: '',
                    filterDateEnd: '',
                    filterDate: new Date().getTime()
                }
            },
            mounted() {
                // 微信小程序需要用此写法
                this.$refs.datetimePicker.setFormatter(this.formatter);
            },
            methods: {
                filterDateChange(e) {
                    console.log('---- filterDateChange ----:', e);
                    this.filterForm.regDate = e.detail.value;
                },
                filterReset() {

                },
                filterSubmit() {

                },
                fliterFormToggle() {
                    this.filterFormVisible = !this.filterFormVisible;
                },
                pickerDate(type) {
                    this.showDatePicker = true;
                    // 标志变量,用于区分是哪个表单触发的
                    this.dateType = type;
                },
                confirmFilterDate() {
                    console.log('---- confirmFilterDate ----:', this.filterDate);
                    // 通过标志变量,把值赋值到正确的表单上
                    this[this.dateType] = e.value;
                    this.showDatePicker = false;
                },
                formatter(type, value) {
                    if (type === 'year') {
                        return `${value}年`
                    }
                    if (type === 'month') {
                        return `${value}月`
                    }
                    if (type === 'day') {
                        return `${value}日`
                    }
                    return value
                },
            }
	}
</script>

// ......

坑位

但是事与愿违,在confirm回调里取到v-model绑定的值不是最新的,而是上一次的值,更奇怪的是你点击二次就能拿到最新的值了

Why?

通过看了DatetimePicker的源码发现,引起这个问题的原因很简单,因为它的代码是先触发confirm回调,再才触发input事件去更新v-model上的值,当你在confirm回调去取值的时候,值还没有更新,点第二次可以那是因为值已经更新了。

组件源码片段如下:

......

// 点击工具栏的确定按钮
confirm() {
	this.$emit('confirm', {
		value: this.innerValue,
		mode: this.mode
	})
	this.$emit('input', this.innerValue)
},
......

解决方案

方案1

既然发现是因为更新延后,我们可以加个定时器来获取

示例代码片段:

......
confirmFilterDate() {
		console.log('---- confirmFilterDate ----:0', this.filterDate);
		setTimeout(() => {
			console.log('---- confirmFilterDate ----:1', this.filterDate);
			// 通过标志变量,把值赋值到正确的表单上
			this[this.dateType] = this.filterDate;
			this.showDatePicker = false;
		}, 0);
	},
......

方案2

通过vue自带的$nextTick来解决

示例代码片段:

......
confirmFilterDate() {
	console.log('---- confirmFilterDate ----:0', this.filterDate);
	this.$nextTick(() => {
		console.log('---- confirmFilterDate ----:1', this.filterDate);
		this[this.dateType] = this.filterDate;
		this.showDatePicker = false;
	})
},
......

方案3(目前推荐)

其实可以不用依赖v-model值,confirm回调有把最新的日期选择值传回来,直接取那个值即可

示例代码片段:

confirmFilterDate(e) {
	console.log('---- confirmFilterDate ----:0', this.filterDate, e);
	this[this.dateType] = e.value;
	this.showDatePicker = false;
},

或者可以再等等,我有向uView官方提了一个PR github.com/umicro/uVie… 希望能优化这一个问题,如果能合并那后续就可以在confirm回调里愉快的获取v-model最新值了。