vue 自定义组件 v-model
官方的介绍讲得比较全,这里就省去复制粘贴的步骤了。此处模拟一种非表单元素的 v-model 组件:
类似复选框,在组件里点选不同的选项,然后能跟父组件双向绑定。
1. 首先做好基础的排版及样式
<template> <div style="padding:10px"> <span>1</span> <span>2</span> <span>3</span> <span>4</span> </div> </template> <style scoped> span{ display: inline-block; width: 80px; height: 30px; line-height: 30px; border-radius: 4px; border: 1px solid #333; color: #333; text-align: center; } span.active{ border-color: #f45619; color: #f45619; } </style>
2. 添加 model 属性
根据文档内容,v-model 需要两个属性:value、event,一个用来传值(父传子),一个用来接收并赋值(子传父)。
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value" >
我们定为 val、change 以示区分
<script> export default { model:{ prop:'val', event:'change' }, // val 这个值本身是不存在的,所以需要通过 props 定义 props:{ val: Array, }, }; </script>
3. 接下来,我们把数据进一步处理,通过 computed 计算属性操作父组件传过来的值。val 为 0,1,2,3 中的值组成的数组,如:[ 1,3 ]。通过 computed 转换为 list : [ 0,1,0,1]
<template> <div style="padding:10px"> <span :class="{'active':list[0]}" >1</span> <span :class="{'active':list[1]}" >2</span> <span :class="{'active':list[2]}" >3</span> <span :class="{'active':list[3]}" >4</span> </div> </template> <script> export default { model:{ prop:'val', event:'change' }, // val 这个值本身是不存在的,所以需要通过 props 定义 props:{ val: Array, }, computed:{ list(){ let arr = [] this.val.forEach(i=>{ let n = parseInt(i) arr[n] = 1 }) return arr } }, }; </script> <style scoped> span{ display: inline-block; width: 80px; height: 30px; line-height: 30px; border-radius: 4px; border: 1px solid #333; color: #333; text-align: center; } span.active{ border-color: #f45619; color: #f45619; } </style>
4. 到这里实现了单向的绑定,然后根据子组件的事件操作给父组件传值。通过子组件的点击事件触发父组件的事件提交,并把新值带给父组件
<template> <div style="padding:10px"> <span :class="{'active':list[0]}" @click="onTap(0)">1</span> <span :class="{'active':list[1]}" @click="onTap(1)">2</span> <span :class="{'active':list[2]}" @click="onTap(2)">3</span> <span :class="{'active':list[3]}" @click="onTap(3)">4</span> </div> </template> <script> export default { model:{ prop:'val', event:'change' }, // val 这个值本身是不存在的,所以需要通过 props 定义 props:{ val: Array, }, computed:{ list(){ let arr = [] this.val.forEach(i=>{ let n = parseInt(i) arr[n] = 1 }) return arr } }, methods:{ onTap(n){ console.log(n) let list = this.list list[n] = !list[n] let res = [] list.map((i, idx)=>{ if(i) res.push(idx) }) // 注意 // 此处的 change ,来自一开始定义在 model 属性里的event
// 父组件在接收到传过来的新值后,会用这个新值更新 val 属性,这样就会更新了自组件的值,完成双向绑定的过程
this.$emit('change',res)
}
}
}; </script> <style scoped> span{ display: inline-block; width: 80px; height: 30px; line-height: 30px; border-radius: 4px; border: 1px solid #333; color: #333; text-align: center; } span.active{ border-color: #f45619; color: #f45619; } </style>
5. 父组件引用:
// 父组件 <template> <div class="hello"> <hello v-model="value"></hello> </div> </template> <script> import hello from '@/components/model' export default { components:{ hello }, data () { return { value:[1,3], } }, } </script>
参考官方文档 :