如果radio只有少量的几个,我们完全可以每个都绑v-model,但如果radio太多的话,代码就显得啰嗦了,我们用radio-group分组来实现。
我们先定义一下radio-group。
group组件功能比较简单,就是把里面的内容用slot完全展示出来。
components/myradiogroup.vue:
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
name: "MyRadioGroup",
props:{
value:null //接收v-model
}
}
</script>
然后注册一下。
main.js:
import Vue from 'vue'
import App from './App.vue'
import MyRadio from './components/myradio'
import MyRadioGroup from './components/myradiogroup'
Vue.config.productionTip = false
Vue.component(MyRadio.name,MyRadio)
Vue.component(MyRadioGroup.name,MyRadioGroup)
new Vue({
render: h => h(App),
}).$mount('#app')
使用起来也比较简单,就是把原来绑定在my-radio上面的v-model,提到了my-radio-group组件上面来。
App.vue
<template>
<div id="app">
<my-radio-group v-model="sex">
<my-radio label="0">男</my-radio>
<my-radio label="1">女</my-radio>
</my-radio-group>
</div>
</template>
<script>
export default {
data(){
return {
sex: '0'
}
}
}
</script>
这就会出现一个问题:
group组件接收到的v-model值,如何传递到里面的radio组件?
我们很自然的想到了$parent,在radio组件通过$parent可以访问到v-model。但是,这种严格的层级关系容易出问题,比如这样的:
<template>
<div id="app">
<my-radio-group v-model="sex">
<my-button>
<my-radio label="0">男</my-radio>
<my-radio label="1">女</my-radio>
</my-button>
</my-radio-group>
</div>
</template>
这种层级关系会让$parent失去作用,我们可以用provide-inject方案来解决。
provide-inject是多层级组件之间信息通信的一种方案,类似我们常用的vuex。
但vuex需额外定义store等好多东西,与具体项目耦合度太高,显然不适合自定义组件场景,因此provide-inject就是最佳替代方案。
我们在group组件定义provide,注意provide跟data属性一样,需使用回调函数。
components/myradiogroup.vue:
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
name: "MyRadioGroup",
provide(){
return {
radioGroup: this
}
},
props:{
value:null //接收v-model
}
}
</script>
我们定义了一个新变量radioGroup,值就是当前这个group组件。
也就是说,我们将group组件整个往下传递,这样在子组件radio(或者更深层次的组件)需要什么,就直接从group组件取就可以了。
components/myradio.vue:
<template>
<label class="my-radio" :class="{'is-checked': label === model}">
<span class="my-radio__input">
<span class="my-radio__inner"></span>
<!--真实的radio,被隐藏,去掉.my-radio__original可以看到-->
<input v-model="model" :name="name" :value="label" class="my-radio__original" type="radio" />
</span>
<span class="my-radio__label">
<slot></slot>
<!--如果没传内容,就用label的值-->
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
export default {
name: "MyRadio",
//=====新增inject========//
inject:{
radioGroup: {
default:''
}
},
computed:{
//============需改造的部分============//
model:{
get(){
//如果有group包裹,就返回group上面的v-model值,否则就是radio上面的v-model
return this.isGroup ? this.radioGroup.value : this.value
},
set(val){
//this.$emit('input', val)
this.isGroup ? this.radioGroup.$emit('input', val) : this.$emit('input', val)
}
},
//=====新增isGroup========//
isGroup(){
// 判断radio是否有group包裹
return !!this.radioGroup //强制转为布尔类型
},
},
props:{
label:{ //接收label
type:[String,Number,Boolean],
default:''
},
value:null, // 接收v-model
name:{ // 有可能传name
type:String,
default:''
}
}
}
</script>
<style lang="scss">
.my-radio {
color: #606266;
font-weight: 500;
line-height: 1;
position: relative;
cursor: pointer;
display: inline-block;
white-space: nowrap;
outline: none;
font-size: 14px;
margin-right: 30px;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
.my-radio__input {
white-space: nowrap;
cursor: pointer;
outline: none;
display: inline-block;
line-height: 1;
position: relative;
vertical-align: middle;
.my-radio__inner {
border: 1px solid #dcdfe6;
border-radius: 100%;
width: 14px;
height: 14px;
background-color: #fff;
position: relative;
cursor: pointer;
display: inline-block;
box-sizing: border-box;
}
.my-radio__original {
opacity: 0;
outline: none;
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
}
}
.my-radio__label {
font-size: 14px;
padding-left: 10px;
}
}
/*增加选中的样式*/
.is-checked {
.my-radio__input{
.my-radio__inner{
border-color:#409eff;
background: #409eff;
&:after{
transform: translate(-50%,-50%) scale(1);
}
}
}
.my-radio__label{
color:#409eff;
}
}
</style>
测试一下。
App.vue:
<template>
<div id="app">
<!--有group包裹-->
<my-radio-group v-model="sex">
<my-radio label="0">男</my-radio>
<my-radio label="1">女</my-radio>
</my-radio-group>
<!--无包裹-->
<my-radio label="0" v-model="sex2">男</my-radio>
<my-radio label="1" v-model="sex2">女</my-radio>
</div>
</template>
<script>
export default {
data(){
return {
sex: '0',
sex2: '1'
}
}
}
</script>
网友评论