前言

根据百度前端技术学院2018 MVVM 学院 中的 2.5 表单实现Input、Checkbox组件 用 vue 实现的

checkbox复选框组件,使checkbox有三种状态: checked:ture (选中) / false (未选中) , indeterminate (部分选中)

1.利用slot插槽

2.在同一文件中用到父子通信 $emit $on

本文章的 github地址


实现思路

1.在mounted(附加到页面上)时绑定监听

因为只有在mounted(元素挂载到页面上)之后才能理清节点之间的关系,也才能找到目标节点的$parent,$children

mounted() {
    // 得先检测其 $parent 是不是复选框组件(如果是,则其有onDispatch 函数)
    if (this.$parent.onDispatch) {
        // 如果组件的状态改变,则会执行其父组件的 onDispatch 函数
        this.$on("checkStatus", this.$parent.onDispatch);
    }
},

 

2. 当复选框状态改变时,将自己的改变后的复选框状态通知给其父复选框

this.$emit("checkStatus", "indeterminate"/true/false);

 

3. onDispatch 函数分析

3.1 接收到的值为 子组件状态改变后 发送过来的状态:{"indeterminate"/true/false}

3.2 改变其#(id).checked属性,直接操作其绑定的isChecked即可:checked="isChecked"

isChecked只有true/false 

3.3 改变其#(id).indeterminate属性,得获取元素(需要id,所以每个chekbox需要绑定一个id:id="id"),得到其indeterminate,

_getIndeterminate() {
    return document.getElementById(this.id).indeterminate;
},
_setIndeterminate(bool) {
    document.getElementById(this.id).indeterminate = bool;
},

 

3.4 当复选框状态为选中/未选中 时,设置其全部子组件为相对应值

(递归,多层改变,能对孩子,孙子,重孙等起作用。)

_setAllSubItem(component, bool) {
    component.$children.map(i => {
        i.isChecked = bool;
        this._setAllSubItem(i, bool);
    });
},

 

4. 最后执行形式

<v-checkbox text="爱好" id="hobby">
    <v-checkbox text="种花" id="plant">
        <v-checkbox text="养鱼" id="fish"></v-checkbox>
    </v-checkbox>
    <v-checkbox text="购物" id="shop"></v-checkbox>
    <v-checkbox text="吃饭" id="eat"></v-checkbox>
</v-checkbox>

 

代码实现

<template>
  <div>
    <label>
      <input type="checkbox" @change="handleChange" :checked="isChecked" :id="id">
      <span class="checkbox"></span>
      <span class="text" s-if="text">{{text}}</span>
    </label>
    <div class="sub">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
  props: ["text", "id"],
  mounted() {
    if (this.$parent.onDispatch) {
      this.$on("checkStatus", this.$parent.onDispatch);
    }
  },
  data() {
    return {
      id: this.id,
      parent: null,
      subItems: [],
      isChecked: false
    };
  },
  methods: {
    // 设置元素 Indeterminate 状态,若为true,表示部分选中
    _setIndeterminate(bool) {
      document.getElementById(this.id).indeterminate = bool;
    },

    // 获取元素 Indeterminate 状态
    _getIndeterminate() {
      return document.getElementById(this.id).indeterminate;
    },

    // 将元素所有子元素的选中状态统一设置成该元素的选中状态 true/false
    _setAllSubItem(component, bool) {
      component.$children.map(i => {
        i.isChecked = bool;
        this._setAllSubItem(i, bool);
      });
    },

    onDispatch(val) {
      //不是根元素
      // console.log(this.$parent.$parent._uid);
      console.log(this);
      // 恢复默认状态
      this._setIndeterminate(false);
      // 如果子组件传过来的值是"indeterminate",即子组件是部分选中状态,那么其父组件一定也是部分选中状态
      if (val == "indeterminate") {
        this._setIndeterminate(true);
        this.$emit("checkStatus", "indeterminate");
        return;
      }
      var subItems = this.$children;
      if (subItems.length > 0) {
        var check_num = 0;
        subItems.map(i => {
          // 或者如果子选项中有一个为indeterminate,那么其父组件也一定也是部分选中状态
          if (i._getIndeterminate()) {
            this._setIndeterminate(true);
            this.$emit("checkStatus", "indeterminate");
            return;
          }
          if (i.isChecked) {
            check_num++;
          }
        });
        if (check_num == subItems.length) {
          //选中子项目个数
          this.isChecked = true;
          this.$emit("checkStatus", true);
        } else if (check_num == 0) {
          this.isChecked = false;
          this.$emit("checkStatus", false);
        } else {
          this._setIndeterminate(true);
          this.$emit("checkStatus", "indeterminate");
        }
      }
    },

    handleChange() {
      this.isChecked = !this.isChecked;
      this.$emit("checkStatus", this.isChecked);
      if (this.isChecked) {
        this._setAllSubItem(this, true);
      } else {
        this._setAllSubItem(this, false);
      }
    }
  }
};

 

结束

如果文章对你有帮助,麻烦点赞哦!一起走码农花路~

 

Logo

前往低代码交流专区

更多推荐