前情
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"></text></u-input><view class="filter-connect"></view><u-input v-model="filterDateEnd" placeholder="选择结束时间" readonly @click.native="pickerDate('filterDateEnd')"><text slot="prefix" class="iconfont"></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:https://github.com/umicro/uView2.0/pull/1068 希望能优化这一个问题,如果能合并那后续就可以在confirm回调里愉快的获取v-model最新值了。