Element-UI源码——Radio单选框

Radio单选框

radio.vue

<template>

  <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>
      <!-- v-model绑定的是checked和change -->
      <input
        ref="radio"
        class="el-radio__original"
        :value="label"
        type="radio"
        aria-hidden="true"
        v-model="model"
        @focus="focus = true"
        @blur="focus = false"
        @change="handleChange"
        :name="name"
        :disabled="isDisabled"
        tabindex="-1"
        autocomplete="off"
      >
    </span>
    <span class="el-radio__label" @keydown.stop>
      <slot></slot>
      <!-- 没有默认插槽时显示 -->
      <template v-if="!$slots.default">{{label}}</template>
    </span>
  </label>
</template>
<script>
  import Emitter from 'element-ui/src/mixins/emitter';

  export default {
    name: 'ElRadio',
    // 混入
    mixins: [Emitter],
    // 注入elForm和elFormItem对象
    inject: {
      elForm: {
        default: ''
      },

      elFormItem: {
        default: ''
      }
    },

    componentName: 'ElRadio',

    props: {
      value: {},
      // Radio 的 value
      label: {},
      // 是否禁用
      disabled: Boolean,
      // 原生 name 属性
      name: String,
      // 是否显示边框
      border: Boolean,
      // Radio 的尺寸,仅在 border 为真时有效
      size: String
    },

    data() {
      return {
        focus: false
      };
    },
    computed: {
      // 向上查找父节点,看父节点的自定义组件名是否等于按钮组,找到时设置到_radioGroup上
      isGroup() {
        let parent = this.$parent;
        while (parent) {
          if (parent.$options.componentName !== 'ElRadioGroup') {
            parent = parent.$parent;
          } else {
            this._radioGroup = parent;
            return true;
          }
        }
        return false;
      },
      // 组件值 如果存在按钮组设置为按钮组的value,否则设置自己的value
      model: {
        get() {
          return this.isGroup ? this._radioGroup.value : this.value;
        },
        // 在设置按钮值时如果存在按钮组,触发混入的dispatch方法,调用按钮组的input事件
        // 不属于则触发自身的input事件设置值外层的v-model绑定的是value和input事件将val值传递到外边
        set(val) {
          if (this.isGroup) {
            this.dispatch('ElRadioGroup', 'input', [val]);
          } else {
            this.$emit('input', val);
          }
          // 如果外界没有通过v-model设置值 则不会触发input事件 这时通过判断model和label是否相等来设置radio的原生checked值
          this.$refs.radio && (this.$refs.radio.checked = this.model === this.label);
        }
      },
      _elFormItemSize() {
        return (this.elFormItem || {}).elFormItemSize;
      },
      // 设置按钮的大小
      radioSize() {
        const temRadioSize = this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
        return this.isGroup
          ? this._radioGroup.radioGroupSize || temRadioSize
          : temRadioSize;
      },
      // 设置禁用
      isDisabled() {
        return this.isGroup
          ? this._radioGroup.disabled || this.disabled || (this.elForm || {}).disabled
          : this.disabled || (this.elForm || {}).disabled;
      },
      tabIndex() {
        // disabled情况下不能被tab选中 如果存在按钮组只有在当前被选中情况下才能被tab导航选中
        return (this.isDisabled || (this.isGroup && this.model !== this.label)) ? -1 : 0;
      }
    },

    methods: {
      // 当触发点击事件时触发自身的change事件设置值 如果有按钮组 触发按钮组的change事件并设置值
      handleChange() {
        this.$nextTick(() => {
          this.$emit('change', this.model);
          this.isGroup && this.dispatch('ElRadioGroup', 'handleChange', this.model);
        });
      }
    }
  };
</script>

其中混入的Emitter

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;
        }
      }
      // 如果存在父节点 调用父节点的方法
      // 例如radio的handleChange方法中this.dispatch('ElRadioGroup', 'handleChange', this.model)
      // 调用按钮组的handleChange方法,传入this.model参数
      if (parent) {
        // apply将参数以数组的形式传入 call将参数分别传入
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

Radio Attributes

参数说明类型可选值默认值
value / v-model绑定值string / number / boolean
labelRadio 的 valuestring / number / boolean
disabled是否禁用booleanfalse
border是否显示边框booleanfalse
sizeRadio 的尺寸,仅在 border 为真时有效stringmedium / small / mini
name原生 name 属性string

Radio Events

事件名称说明回调参数
input绑定值变化时触发的事件选中的 Radio label 值

radio-group.vue

<template>
  <component
    :is="_elTag"
    class="el-radio-group"
    role="radiogroup"
    @keydown="handleKeydown"
  >
    <slot></slot>
  </component>
</template>
<script>
  import Emitter from 'element-ui/src/mixins/emitter';
// 上下左右四个按键的keyCode 冻结对象使对象无法修改
  const keyCode = Object.freeze({
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40
  });
  export default {
    name: 'ElRadioGroup',
    componentName: 'ElRadioGroup',
    inject: {
      elFormItem: {
        default: ''
      }
    },
    mixins: [Emitter],
    props: {
      // 绑定值 传给子组件 按钮使用
      value: {},
      // 单选框组尺寸,仅对按钮形式的 Radio 或带有边框的 Radio 有效
      size: String,
      // 按钮形式的 Radio 激活时的填充色和边框色
      fill: String,
      // 按钮形式的 Radio 激活时的文本颜色
      textColor: String,
      // 是否禁用
      disabled: Boolean
    },
    computed: {
      _elFormItemSize() {
        return (this.elFormItem || {}).elFormItemSize;
      },
      // 标签 默认div
      _elTag() {
        let tag = (this.$vnode.data || {}).tag;
        if (!tag || tag === 'component') tag = 'div';
        return tag;
      },
      radioGroupSize() {
        return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
      }
    },
    created() {
      this.$on('handleChange', value => {
        this.$emit('change', value);
      });
    },
    mounted() {
      // 当radioGroup没有默认选项时,第一个可以选中Tab导航
      const radios = this.$el.querySelectorAll('[type=radio]');
      const firstLabel = this.$el.querySelectorAll('[role=radio]')[0];
      if (![].some.call(radios, radio => radio.checked) && firstLabel) {
        firstLabel.tabIndex = 0;
      }
    },
    methods: {
      handleKeydown(e) { // 左右上下按键 可以在radio组内切换不同选项
        const target = e.target;
        const className = target.nodeName === 'INPUT' ? '[type=radio]' : '[role=radio]';
        const radios = this.$el.querySelectorAll(className);
        const length = radios.length;
        // 找到索引值
        const index = [].indexOf.call(radios, target);
        const roleRadios = this.$el.querySelectorAll('[role=radio]');
        switch (e.keyCode) {
          case keyCode.LEFT: 
          case keyCode.UP:
            // 取消冒泡
            e.stopPropagation();
            // 取消默认事件
            e.preventDefault();
            if (index === 0) {
              roleRadios[length - 1].click();
              roleRadios[length - 1].focus();
            } else {
              roleRadios[index - 1].click();
              roleRadios[index - 1].focus();
            }
            break;
          case keyCode.RIGHT:
          case keyCode.DOWN:
            if (index === (length - 1)) {
              e.stopPropagation();
              e.preventDefault();
              roleRadios[0].click();
              roleRadios[0].focus();
            } else {
              roleRadios[index + 1].click();
              roleRadios[index + 1].focus();
            }
            break;
          default:
            break;
        }
      }
    },
    watch: {
      value(value) {
        this.dispatch('ElFormItem', 'el.form.change', [this.value]);
      }
    }
  };
</script>


Radio-group Attributes

参数说明类型可选值默认值
value / v-model绑定值string / number / boolean
size单选框组尺寸,仅对按钮形式的 Radio 或带有边框的 Radio 有效stringmedium / small / mini
disabled是否禁用booleanfalse
text-color按钮形式的 Radio 激活时的文本颜色string#ffffff
fill按钮形式的 Radio 激活时的填充色和边框色string#409EFF
  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值