We often use the Radio component in the actual development, this component usage and implementation is relatively simple, but to open the source code of element-UI, there are still some good details worth learning.

  • Think about a couple of questions

We thought about the following questions: First, why can we use v-Models directly on custom components? Second, how do we pass parameters across components when using

?

  • <el-radio>The implementation of the

The core of radio component is to use the original label and input label to achieve, and then display different states according to different CSSclass. The HTML is as follows:

<label
    class="el-radio"
    :class="[ border && radioSize ? 'el-radio--' + radioSize : '', { 'is-disabled': isDisabled }, { 'is-focus': focus }, { 'is-bordered': border }, { 'is-checked': model === label } ]"
    role="radio"
    :aria-checked="model === label"
    :aria-disabled="isDisabled"
    :tabindex="tabIndex"
    @keydown.space.stop.prevent="model = isDisabled ? model : label"
  >
    <span class="el-radio__input"
      :class="{ 'is-disabled': isDisabled, 'is-checked': model === label }"
    >
      <span class="el-radio__inner"></span>
      <input
        ref="radio"
        class="el-radio__original"
        :value="label"
        type="radio"
        aria-hidden="true"
        v-model="mdel"
        @focus="focous = true"
        @blur="focus = false"
        @change="handleChange"
        :name="name"
        :disabled="isDisabled"
        tabindex="1"
      >
    </span>
    <span class="el-radio__label" @keydown.stop>
      <slot></slot>
      <template v-if=! "" $slots.default">{{label}}</template>
    </span>
  </label>
Copy the code

We can see that depending on the class name we can display the corresponding style. Aria-checked and aria-disabled are some of the new attributes in HTML5. Tabindex specifies the tab-control order of elements for screen readers. We all know how the Radio component is used:

<template> <el-radio V-model ="radio" label="1"> Optional </el-radio> <el-radio V-model ="radio" label="2"> Optional </el-radio> </template> <script> export default { data () { return { radio: '1' }; } } </script>Copy the code

So how do you know which radio is currently choosing? We just need to check that the value of radio is the same as that of the label passed in. The response logic in the source code is:

.model: {
    get() {
      return this.isGroup ? this._radioGroup.value : this.value;
    },
    set(val) {
      if (this.isGroup) {
        this.dispatch('ElRadioGroup'.'input', [val]);
      } else {
        this.$emit('input', val);
      }
      this.$refs.radio && (this.$refs.radio.checked = this.model === this.label); }}...Copy the code

How does
listen for this change in value? $emit(‘input’, val); $emit(‘input’, val); , so that the changed value can be synchronized. So why is it ok to write it like this? This is the implementation logic of the V-Model, which is essentially equivalent to the following code:

<template>
  <el-radio :value="radio" @input="changeValue" label="1">Alternatives to the</el-radio>
  <el-radio :value="radio" @input="changeValue" label="2">Alternatives to the</el-radio>
</template>

<script>
  export default {
    data () {
      return {
        radio: '1'
      };
    },
    methods: {changeValue(val){
           this.radio = val; }}}</script>
Copy the code

So the V-Model is just a syntactically sugar for the above, sending out an input event so we can use the V-Model on our custom components. Okay, so we can take care of the first one.

  • <el-radio-group>The implementation of the

This component is an extension of the Radio component and is used as follows:

<template>
  <el-radio-group v-model="radio">
    <el-radio :label="3">Alternatives to the</el-radio>
    <el-radio :label="6">Alternatives to the</el-radio>
    <el-radio :label="9">Alternatives to the</el-radio>
  </el-radio-group>
</template>

<script>
  export default {
    data () {
      return {
        radio: 3}; }}</script>
Copy the code

In the element-UI source code, all the logic is written in the Radio component. First, we need to determine whether this is a single box group component.

 isGroup() {
    let parent = this.$parent;
    while (parent) {
      if(parent.$options.componentName ! = ='ElRadioGroup') {
        parent = parent.$parent;
      } else {
        this._radioGroup = parent;
        return true; }}return false;
}
Copy the code

Loop up to the parent component to see if there is a component named ElRadioGroup or a checkbox group. That’s when you initialize it. The value of model will be _this._radiogroup.value. So how do you synchronize the value of the selected child component to the parent component? We noticed that when the model changed, there was this logic:

if (this.isGroup) {
    this.dispatch('ElRadioGroup'.'input', [val]);
  } else {
    this.$emit('input', val); }}Copy the code

So let’s look at the dispatch method. This method is written in mixins as follows:

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)); }}Copy the code

The logic is to find the target parent and distribute the input method. Why loop? Because we might have multiple parent components, such as:

 <el-radio-group v-model="radio">
   <div>
        <el-radio :label="3">Alternatives to the</el-radio>
        <el-radio :label="6">Alternatives to the</el-radio>
        <el-radio :label="9">Alternatives to the</el-radio>
   </div>
  </el-radio-group>
Copy the code

Once distributed, the bound radio value can be updated. Let’s look at the el-radio-group and the change event. According to the above learning, it becomes very simple, and the code is as follows:

handleChange() {
  this.$nextTick(() = > {
    this.$emit('change'.this.model);
    this.isGroup && this.dispatch('ElRadioGroup'.'handleChange'.this.model);
  });
}
Copy the code

Well, the above two questions are solved, that is their true veil, we look forward to the unveiling of more unknown veil.