使用Vue.js不确定复选框

时间:2017-04-15 20:41:02

标签: javascript checkbox vue.js

我刚刚开始使用Vue,我正在尝试可视化嵌套列表。

列表项应包含三态复选框: 选中子项时,父项的复选框应变为“不确定”。选中所有子复选框后,还应选中父复选框。 选中父项复选框后,也应选中所有子项复选框(也包括更深嵌套的复选框)。

我有一个有效的解决方案(请查看此pen或下面的代码),但复选框逻辑仍有缺陷。对于此示例,复选框为绿色,不确定框为橙色,未选中框为红色。

我已经没有想法如何解决它。有人可以说明如何在Vue中实现这一目标吗?

REGEDIT

1 个答案:

答案 0 :(得分:1)

更新:修复了@PhillSlevin发现的错误。请参阅pen此处

检查this pen,这是你想要达到的目标吗? 我认为您可以使用eventbusvuex来解决此问题,
如果你将每个部分视为一个组成部分。

'use strict';

var bus = new Vue();

var book = {
  "title": "Book",
  "children": [{
    "title": "1 First title",
    "children": [{
      "title": "1.1 Subtitle"
    }, {
      "title": "1.2 Subtitle"
    }]
  }, {
    "title": "2 Second title",
    "children": [{
      "title": "2.1 Subtitle",
      "children": [{
        "title": "2.1.1 Sub-Sub title"
      }, {
        "title": "2.1.2 Another sub-sub title"
      }]
    }]
  }]
};

Vue.component('book', {
  template: `
<div class="book__chapter">
  <p :class="'book__title ' + status" @click="clickEvent">{{title}} {{parent}}</p>
  <book v-for="child in children" :key="child" :info="child"></book>
</div>
`,
  props: ['info'],
  data() {
    return {
      parent: this.info.parent,
      title: this.info.title,
      children: [],
      status: this.info.status,
    };
  },
  created() {
    const info = this.info;
    if(info.children) {
      info.children.forEach(child => {
        child.status = "unchecked";
        // use title as ID
        child.parent = info.title;
      });
      this.children = info.children;
    }
  },
  mounted() {
    const vm = this;
    bus.$on('upside', (payload) => {
      const targetArr = vm.children.filter((child) => child.title === payload.from);
      if (targetArr.length === 1) {
        const target = targetArr[0];
        target.status = payload.status;
        if (vm.children.every(ele => ele.status === 'checked')) {
          vm.status = 'checked';
        } else if (vm.children.every(ele => ele.status === 'unchecked')) {
          vm.status = 'unchecked';
        } else {
          vm.status = 'indeterminate';
        }
        bus.$emit('upside', {
          from: vm.title,
          status: vm.status,
        });
      }
    });
    
    bus.$on('downside', (payload) => {
      if (payload.from === this.parent) {
        if (payload.status === 'checked') {
          vm.status = 'checked';
          vm.children.forEach(child => child.status = 'checked');
        } else if (payload.status === 'unchecked') {
          vm.status = 'unchecked';
          vm.children.forEach(child => child.status = 'unchecked')
        }
        bus.$emit('downside', {
          from: vm.title,
          status: vm.status,
        })
      }
    });
  },
  methods: {
    clickEvent() {
      if (this.status === 'checked') {
        this.status = 'unchecked';
        this.children.forEach(child => child.status = 'unchecked');
      } else {
        this.status = 'checked';
        this.children.forEach(child => child.status = 'checked');
      }
      
      const vm = this;
      bus.$emit('upside', {
        from: vm.title,
        status: vm.status,
      });
      bus.$emit('downside', {
        from: vm.title,
        status: vm.status,
      });
    },
  }
});

var parent = new Vue({
  el: "#container",
  data: function() {
    return {
      book
    };
  },
});
.book__title.unchecked::after {
  content: '□';
}

.book__title.indeterminate::after {
  content: '△';
}

.book__title.checked::after {
  content: '■';
}

.book__chapter {
  display: block;
  position: reletive;
  margin-left: 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.js"></script>
<div id="container">
  <book :info="book" :parent="'container'"></book>
</div>