uni-app picker组件添加 v-model功能
文章目录前言一、核心思想二、封装组件1.初步封装2.完善组件三、组件使用前言使用 uni-app 开发的时候,经常会使用到 picker 选择器,官方自带的 picker组件必须通过change事件进行赋值,如果支持 v-model 的话,开发起来会更加便利。这篇文章便对 uni-app 中的picker组件进行简单二次封装,使其支持 v-model 指令。一、核心思想Vue中的v-model实际
前言
使用 uni-app 开发的时候,经常会使用到 picker 选择器,官方自带的 picker组件必须通过change事件进行赋值,如果支持 v-model 的话,开发起来会更加便利。
这篇文章便对 uni-app 中的picker组件进行简单二次封装,使其支持 v-model 指令。
一、核心思想
Vue中的v-model实际上是一个语法糖:
<input v-model="key" />
<!-- 等同于 -->
<input :value="key" @input="key=$event.target.value" />
二、封装组件
1.初步封装
我们希望在父组件中使用以下方式调用,且不需要通过change事件,实现 currentValue 的值跟随picker组件的选择而改变:
<!-- 父组件 -->
<myPicker :options="options" v-model="currentValue"></myPicker>
options = ["广东","上海","北京","深圳"]
在子组件中,我们仅需要在原有picker组件的条件下,接收 value 参数并监听 input 方法,就可以实现双向绑定的效果了:
<!-- 子组件 -->
<template>
<picker @change="handleChange" :value="index" :range="options">
{{options[index] || placeholder}}
</picker>
</template>
<script>
export default {
name: "myPicker",
props: {
value: String | Number,
options: Array,
placeholder: {
type: String,
default: "请选择"
}
},
data() {
return {
index: -1
};
},
watch:{
// 解决异步加载带来的回显问题
value(val){
this.index = this.options.findIndex(item => item == val);
}
},
methods: {
handleChange(e) {
this.index = e.target.value;
let currentValue = this.options[this.index];
this.$emit("input", currentValue)
// 用户可以通过监听 @change 事件,在选中的时候进行额外操作
this.$emit("change", currentValue)
},
}
}
</script>
效果图:
2.完善组件
在实际开发中,picker组件的options数据大多是数组对象的形式,纯数组的情况会相对少一些(例如:选中值返回id),我们刚刚封装的组件虽然支持 v-model ,但并不支持这种较为复杂的数据格式,这个时候我们就需要对它进行完善,让这个组件支持两种options格式的数据。
我们将上述例子的options改成以下形式:
options: [
{
value: "gd",
label: "广东"
},
{
value: "sh",
label: "上海"
},
{
value: "bj",
label: "北京"
},
{
value: "sz",
label: "深圳"
}
]
用户传递该数组的时候,picker选择器展示 label 的值,选中值为对应的value。
在子组件中,我们必须多接收两个参数,一个是数据展示的文本,一个是返回数据的字段:
props: {
// 传label则说明组件会将数组对象中label的文本展示出来
rangeKey: String,
// 默认情况下,会将label对应的value字段返回出去,可以根据自己的数据格式进行传递
rangeValue: {
type: String,
default: "value"
}
}
子组件中,options为纯数组的情况下,我们可以通过 options[index] 来展示picker组件所选中的值,而数组对象则不能这么做,我们可以通过一个计算属性来展示当前选中的值:
computed: {
currentValue() {
if (this.rangeKey) {
// index为-1的时候会报错,需要对其进行特殊处理
return this.index == -1 ? "" : this.options[this.index][this.rangeKey]
} else {
return this.options[this.index]
}
}
}
两种数据格式下,改变返回的值也不同,因此子组件中的change事件和watch事件必须做对应处理:
// watch事件
watch: {
value(val) {
this.index = this.rangeKey ? this.options.findIndex(item => item[this.rangeValue] == val) : this.options.findIndex(item => item == val);
}
},
// chang事件
handleChange(e) {
this.index = e.target.value;
let currentValue = this.rangeKey ? this.options[this.index][this.rangeValue] : this.options[this.index];
this.$emit("input", currentValue)
this.$emit("change", currentValue)
}
效果图:
到这里就完成了一个带 v-model 指令的picker组件了,官方的picker组件有许多中mode,这里仅封装selector(单列选择器),有需要的话按照这个思路也可以将所有的模式封装进去,不过更加推荐多种模式分开封装,以便后期的维护。
三、组件使用
父组件:
<!-- 纯数组形式 -->
<myPicker :options="options" v-model="currentValue"></myPicker>
<!-- 数组对象形式 -->
<myPicker :options="options" v-model="currentValue" rangeKey="label"></myPicker>
子组件代码:
<template>
<picker @change="handleChange" :value="index" :range="options" :range-key="rangeKey">
{{currentValue || placeholoder}}
</picker>
</template>
<script>
export default {
name: "myPicker",
props: {
value: String | Number,
options: Array,
rangeKey: String,
rangeValue: {
type: String,
default: "value"
},
placeholoder: {
type: String,
default: "请选择"
}
},
data() {
return {
index: -1
};
},
computed: {
currentValue() {
if (this.rangeKey) {
return this.index == -1 ? "" : this.options[this.index][this.rangeKey]
} else {
return this.options[this.index]
}
}
},
watch: {
value(val) {
this.index = this.rangeKey ? this.options.findIndex(item => item[this.rangeValue] == val) : this.options.findIndex(item => item == val);
}
},
methods: {
handleChange(e) {
this.index = e.target.value;
let currentValue = this.rangeKey ? this.options[this.index][this.rangeValue] : this.options[this.index];
this.$emit("input", currentValue)
this.$emit("change", currentValue)
},
}
}
</script>
组件内部并没有编写样式,大家可以根据自己需求进行编写!
更多推荐
所有评论(0)