七、封装一个element-ui风格的radio组件
前置知识点:
- radio的基本使用
参数支持:
参数名称 | 参数描述 | 参数类型 | 默认值 |
v-model | 双向绑定 | 布尔类型 | false |
label | 单选框和value值 | string,num,Boolean | ' ' |
name | na'm | | |
7.1radio组件的基本框架和样式
框架、基本样式以及选中样式:
<template>
<label class="one-radio is-checke">
<span class="one-radio_input">
<span class="one-radio_inner"></span>
<input
type="radio"
class="one-radio_original"
>
</span>
<span class="one-radio_label">我是label</span>
</label>
</template>
<script>
export default {
name: 'oneRadio',
props: {}
},
watch: {},
data () {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped>
.one-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;
-moz-user-select: none;
.one-radio_input{
white-space: nowrap;
cursor: pointer;
outline: none;
display: inline-block;
line-height: 1;
position: relative;
vertical-align: middle;
.one-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;
&:after{
width: 4px;
height: 4px;
border-radius: 100%;
background-color: #fff;
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%) scale(0);
transition: transform .15s ease-in;
}
}
.one-radio_original{
opacity: 0;
outline: none;
position: absolute;
z-index: -1;
top: 0;
left: 0px;
right: 0;
bottom: 0;
margin: 0;
}
}
.one-radio_label{
font-size: 14px;
padding-left: 10px;;
}
}
// 选中的样式
.one-radio.is-checked{
.one-radio_input{
.one-radio_inner{
border-color: #409eff;
background-color: #409eff;
&:after{
transform: translate(-50%,-50%) scale(1);
}
}
}
.one-radio_label{
color:#409eff;
}
}
</style>
7.2radio组件的数据双向绑定
实现radio组件的数据双向绑定,除了要绑定数据本身外,还要控制radio组件的样式。
实现radio组件数据的绑定,需要父组件传递的label值和value值,其中value值使用v-model语法糖来绑定。
<one-radio v-model="gender" label="0">男</one-radio>
<one-radio v-model="gender" label="1">女</one-radio>
子组件接收数据后,要对数据进行处理。
当radio组件被点击时,绑定的数据应该变为该组件的label值。我们将组件中的input标签的value绑定为传入的label值,并且声明一个计算属性model双向绑定到input组件上,model我们需要通过get方法获取值;并且通过set方法将值回调给父组件。
同时,当我们在点击radio组件时,我们应该让被选中的组件添加选中样式,我们通过label和value的比较来判断,如果相同则显示选中样式。
<template>
<label class="one-radio" :class="{'is-checked': label == value}">
<span class="one-radio_input">
<span class="one-radio_inner"></span>
<input
type="radio"
class="one-radio_original"
:value="label"
v-model="model"
>
</span>
<span class="one-radio_label">
<slot></slot>
<!-- 如果没有传值,就把label作为文本显示 -->
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
//计算属性
computed: {
model: {
get () {
return this.value
},
set (value) {
// 触发父组件的input事件
this.$emit('input', value)
}
}
},
八、封装一个element-ui风格的radio-group组件
radio-group组件是再radio组件上进行优化的,它的目的是在我们使用radio组件时,不必给每个组件都添加一个v-model,而是通过绑定一个v-model来实现数据绑定。
使用radio-group组件包裹radio组件时,需要考虑到的一个问题就是radio-group组件于radio组件之间的通讯。我们在使用radio-group组件时将数据通过v-model进行了绑定,那么raido组件就不能直接拿到这个值,所以我们需要使用provide/inject进行祖孙组件之间得传值。
使用provide/inject非常简单,在radio-group中通过声明provide对象将组件自身进行传递,在radio中使用inject进行接收即可。
radio-group组件架构:
<template>
<div class="one-radio-group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'oneRadioGroup',
provide () {
return {
RadioGroup: this
}
},
props: {
// 组件接收到了value值,我们需要触发这个组件的input事件
// provide 与 inject 用来做祖孙之间得组件通讯
value: null
}
}
</script>
在radio组件中,通过inject可以直接接收到参数,此时,原本通过v-model传递进来的value值,变成了radio-group组件传进来的RadioGroup.value值,所以在computed计算属性中,我们先写一个radio组件是否被radio-group组件进行判断的方法,并且使用在model中,如果被包裹了,则使用RadioGroup.value值,否则使用value值。
同时在is-checked类的判断上抛弃label于value的比较,转而通过label于model(model此时的值为value或RadioGroup.value)比较,来进行样式的更改。
<template>
<label class="one-radio" :class="{'is-checked': label == model}">
<span class="one-radio_input">
<span class="one-radio_inner"></span>
<input
type="radio"
class="one-radio_original"
:value="label"
v-model="model"
>
</span>
<span class="one-radio_label">
<slot></slot>
<!-- 如果没有传值,就把label作为文本显示 -->
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
export default {
name: 'oneRadio',
props: {
label: {
type: [String, Number, Boolean],
defualt: ''
},
value: null,
name: {
type: String,
defualt: ''
}
},
inject: {
RadioGroup: {
default: ''
}
},
computed: {
model: {
get () {
return this.isGroup ? this.RadioGroup.value : this.value
},
set (value) {
// 触发父组件的input事件
this.isGroup ? this.RadioGroup.$emit('input', value) : this.$emit('input', value)
}
},
// 用于判断radio是否被radioGroup包裹
isGroup () {
return !!this.RadioGroup
}
}
}
</script>
--------------------------------------------至此,radio组件与radio-group组件封装完毕--------------------------------------------------
附radio组件代码:
<template>
<label class="one-radio" :class="{'is-checked': label == model}">
<span class="one-radio_input">
<span class="one-radio_inner"></span>
<input
type="radio"
class="one-radio_original"
:value="label"
v-model="model"
>
</span>
<span class="one-radio_label">
<slot></slot>
<!-- 如果没有传值,就把label作为文本显示 -->
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
export default {
name: 'oneRadio',
props: {
label: {
type: [String, Number, Boolean],
defualt: ''
},
value: null,
name: {
type: String,
defualt: ''
}
},
inject: {
RadioGroup: {
default: ''
}
},
computed: {
model: {
get () {
return this.isGroup ? this.RadioGroup.value : this.value
},
set (value) {
// 触发父组件的input事件
this.isGroup ? this.RadioGroup.$emit('input', value) : this.$emit('input', value)
}
},
// 用于判断radio是否被radioGroup包裹
isGroup () {
return !!this.RadioGroup
}
},
data () {
return {}
},
methods: {}
}
</script>
<style lang="scss" scoped>
.one-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;
-moz-user-select: none;
.one-radio_input{
white-space: nowrap;
cursor: pointer;
outline: none;
display: inline-block;
line-height: 1;
position: relative;
vertical-align: middle;
.one-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;
&:after{
width: 4px;
height: 4px;
border-radius: 100%;
background-color: #fff;
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%) scale(0);
transition: transform .15s ease-in;
}
}
.one-radio_original{
opacity: 0;
outline: none;
position: absolute;
z-index: -1;
top: 0;
left: 0px;
right: 0;
bottom: 0;
margin: 0;
}
}
.one-radio_label{
font-size: 14px;
padding-left: 10px;;
}
}
// 选中的样式
.one-radio.is-checked{
.one-radio_input{
.one-radio_inner{
border-color: #409eff;
background-color: #409eff;
&:after{
transform: translate(-50%,-50%) scale(1);
}
}
}
.one-radio_label{
color:#409eff;
}
}
</style>
附radio-group组件代码:
<template>
<div class="one-radio-group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'oneRadioGroup',
provide () {
return {
RadioGroup: this
}
},
props: {
// 组件接收到了value值,我们需要触发这个组件的input事件
// provide 与 inject 用来做祖孙之间得组件通讯
value: null
}
}
</script>
<style lang="scss" scoped>
</style>