This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together.
One, the introduction
Today is also a day full of hope (a day to fish for fish). In my spare time, I looked through the common element UI used in development and found some clever designs in radio components. Let’s have a look at how to implement a radio component.
Second, preparation
Prepare the melon seeds cola small bench
Third, the body
1. radio
Before we get started let’s see how element-UI radio is used. Okay
<template> <el-radio V-model ="radio" label="1"> Optional </el-radio> <el-radio V-model ="radio" label="2"> Optional </el-radio> </template>Copy the code
You can see that our component needs to receive value, label, slot slots and so on, so our idea is to create a basic template as follows
<template>
<div>
<input type="radio">
<div class="radio-radio-label">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
componentName: 'radio-demo',
props:{
label: {
type: String,
default: ""
},
value: {
type: String,
default: ""
}
}
}
</script>
Copy the code
Now let’s think about how to correlate the input with the data that is passed. There are some details to pay attention to
The value in props can receive the value passed in by the V-Model, but this data is one-way and can only be read and cannot be changed, so we need to evaluate the properties to encapsulating the value twice
$emit(‘input’,val) this.$emit(‘input’,val)
V-model: Values are assigned to v-Model binding values when applied to radio tags
Our component can then be written like this
<template>
<div class="radio-container">
<div class="radio-input">
<input
type="radio"
:value="label"
v-model="model"
:checked='model === label'
>
</div>
<div class="radio-radio-label">
<slot></slot>
</div>
</label>
</template>
<script>
export default {
componentName: 'radio-demo',
props:{
label: {
type: String,
default: ""
},
value: {
type: String,
default: ""
}
},
computed:{
model:{
get(){
return this.value
},
set(val){
this.$emit('input', val)
}
}
}
}
</script>
Copy the code
See the effect
Blindy: Yes, not quite. Are you kidding me? This is native radio!
Made a mistake! Again, let’s hide the native input and put our radio div version on it
Let’s dress INPUT in the emperor’s new clothes
.radio-real{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
z-index: -1;
}
Copy the code
Plus the DIV version of radio that we made ourselves
<div class="radio-show" :class="{'is-checked':model === label}">
<div class="radio-inner"></div>
</div>
.radio-show{
border: 1px solid #eee;
border-radius: 50%;
width: 14px;
height: 14px;
display:flex;
align-items: center;
justify-content: center;
.radio-inner{
width: 4px;
height:4px;
border-radius: 50%;
background-color: #fff
}
}
.is-checked{
background-color:rgb(152, 237, 243)
}
Copy the code
At this time we open the page will find, MD why do not move!! Don’t panic, the style tells us that the input is blocked by the upper div.
To solve this problem, the label tag defines a tag for the input tag that triggers the input tag when the user clicks on the label tag
<label role="radio"></label>
Copy the code
So we can change the outermost div to label to solve the masking problem, and let’s see what happens
A simple Radio component is done
2.radio-group
Now that radio is done, let’s see how radio groups are implemented. Let’s see how element-UI radio-groups are used first
<el-radio-group V-model ="radio" @change='handleChange'> <el-radio :label="6"> Alternate </el-radio> <el-radio :label="9"> </el-radio> </el-radio-group>Copy the code
We just need to bind the v-model parameters in the radio-group and trigger the change event in the instance via $emit
<template>
<div class="container">
<slot></slot>
</div>
</template>
<script>
export default {
componentName: "demo-radio-group",
props:{
value: {
type:String,
default:''
}
},
created(){
this.$on('handleChange',val=>{
this.$emit('change',val)
})
}
}
Copy the code
$emit = ‘value’; $emit = ‘change’; $emit = ‘value’; $emit = ‘value’; $emit(‘input’) to change the value of the V-model
Since we can’t get these things in the radio component, we can get the instance of the radio-Group directly in the radio component and operate through the instance of the radio-Group.
Get the radio-Group component in computed
groupVm(){ let parent = this.$parent while (parent){ if(parent.$options.componentName ! == 'demo-radio-group'){ parent = parent.$parent; }else{ return parent; } } return null }Copy the code
Overrides the computed property Model in the Radio component
model:{ get(){ return this.groupVm? this.groupVm.value : this.value }, set(val){ if(this.groupVm){ this.groupVm.$emit('input',val) }else{ this.$emit('input', val) } } }Copy the code
After the operation of these two parts, our radio-group can work normally. Let’s have a look at the effect
<radio-group v-model="val">
<radio label="radio1">1</radio>
<radio label="radio2">2</radio>
</radio-group>
<radio-group v-model="val1" >
<radio label="radio3">3</radio>
<radio label="radio4">4</radio>
</radio-group>
Copy the code
MMM, it tastes right. Let’s go back to our change event.
Since both value and input events can be called directly from a radio call to an instance of the group, change is also possible.
//radio.vue handleChange(){this.groupvm.$emit('change',this.model)} @change="handleChange"> <radio label="radio1">1</radio> <radio label="radio2">2</radio> </radio-group>Copy the code
This is very bad for tracing event delivery, so we can implant a handleChange event in radio-group via ON, execute this.on implant a handleChange event in this event, Execute this.on in this event to embed a handleChange event, execute this.emit(change) in this event
//radio.vue
handleChange(){
this.groupVm.$emit('handleChange',this.model)
}
//radio-group.vue
created(){
this.$on('handleChange',val=>{
this.$emit('change',val)
})
}
Copy the code
This completes our change event
3. emitter
In the process of component fabrication, event operations are often triggered in parent or child instances. Emitter is a tool used to deal with such operations. In VUe2, event calls are made by mixing emitters
function broadcast(componentName, eventName, params) { this.$children.forEach(child => { var name = child.$options.componentName; if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)); } else { broadcast.apply(child, [componentName, eventName].concat([params])); }}); } export default { methods: { dispatch(componentName, eventName, params) { var parent = this.$parent || this.$root; var name = parent.$options.componentName; while (parent && (! name || name ! == componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.componentName; } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); }}};Copy the code
Use:
//radio.vue import Emitter from './emitter.js' export default { mixins:[Emitter] ... {handleChange(){this.$nextTick(() => {this.$emit('change', this.model); this.groupVm && this.dispatch('demo-radio-group', 'handleChange', this.model); }); }}}Copy the code
code
1.radio.vue
<template> <label class="radio-container" role="radio"> <div class="radio-input"> <div class="radio-show" :class="{'is-checked':model === label}"> <div class="radio-inner"></div> </div> <input class="radio-real" type="radio" :value="label" v-model="model" :checked='model === label' @change='handleChange' > </div> <div class="radio-radio-label"> <slot></slot> </div> </label> </template> <script> import Emitter from "./emitter" export default { componentName: 'demo-radio', mixins:[Emitter], props:{ label: { type: String, default: "" }, value: { type: String, default: "" } }, computed:{ groupVm(){ let parent = this.$parent while (parent){ if(parent.$options.componentName ! == 'demo-radio-group'){ parent = parent.$parent; }else{ return parent; } } return null }, model:{ get(){ return this.groupVm? this.groupVm.value : this.value }, set(val){ if(this.groupVm){ this.groupVm.$emit('input',val) }else{ this.$emit('input', val) } } } }, methods:{ handleChange(){ this.$nextTick(() => { this.$emit('change', this.model); this.groupVm && this.dispatch('demo-radio-group', 'handleChange', this.model); }); } } } </script> <style scoped lang='scss'> .radio-container{ display: flex; align-items: center; .radio-input{ position: relative; margin-right: 10px; .radio-show{ border: 1px solid #eee; border-radius: 50%; width: 14px; height: 14px; display:flex; align-items: center; justify-content: center; .radio-inner{ width: 4px; height:4px; border-radius: 50%; background-color: #fff } } .is-checked{ background-color:rgb(152, 237, 243) } .radio-real{ position: absolute; left: 0; right: 0; top: 0; bottom: 0; opacity: 0; z-index: -1; } } } </style>Copy the code
2.radio-group
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
componentName: "demo-radio-group",
props:{
value: {
type:String,
default:''
}
},
created(){
this.$on('handleChange',val=>{
this.$emit('change',val)
})
}
}
</script>
Copy the code
3.emitter.js
function broadcast(componentName, eventName, params) { this.$children.forEach(child => { var name = child.$options.componentName; if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)); } else { broadcast.apply(child, [componentName, eventName].concat([params])); }}); } export default { methods: { dispatch(componentName, eventName, params) { var parent = this.$parent || this.$root; var name = parent.$options.componentName; while (parent && (! name || name ! == componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.componentName; } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } }, broadcast(componentName, eventName, params) { broadcast.call(this, componentName, eventName, params); }}}Copy the code
4. home.vue
<template> <div class="container"> <radio-group v-model="val" @change="handleChange"> <radio label="radio1">1</radio> <radio label="radio2">2</radio> </radio-group> <radio-group v-model="val1" @change="handleChange1"> <radio label="radio3">3</radio> <radio label="radio4">4</radio> </radio-group> </div> </template> <script> import radio from '.. /.. /components/radio/radio.vue' import radioGroup from '.. /.. /components/radio/radio-group.vue' export default { components:{ radio,radioGroup }, data(){ return { val:'', val1:'' } }, methods:{ handleChange(val){ console.log(val); }, handleChange1(val){ console.log(val); } } } </script>Copy the code
Four,
The Radio component in Element-UI is not difficult, and it is a beginner’s component. However, there are many things that can be learned in it, such as the design of groups in the component, the acquisition of attributes and the invocation of events in the instance, and the design concept of emiter tool, which are short and concise.
That’s all for today’s share
I heard that you like to praise, this year’s year-end award to get soft 😍