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 😍