Как исправить vue обновления с помощью флажков? - PullRequest
0 голосов
/ 14 марта 2019

Я пытаюсь сделать древовидное представление с флажками, где родитель всегда находится в правильном состоянии, основываясь на его дочерних элементах.Включая неопределенные значения.И v-модель это значения в массив.Это то, что я получил до сих пор

Проблема в том, что я установил B2 (листовой узел), чтобы выбрать, но потом я ожидаю, что его родитель и родитель становятся неопределенными.Однако это не так.Я полагаю, что это потому, что я использую v-if (где у меня v-if="data.expanded"), а не v-show в дочерних элементах дерева, и у меня свернут родительский элемент B2, поэтому он не отображается.

Но я не понимаю, почему это проблема, потому что я делаю изменения только в модели данных и выполняю события emit в виртуальном DOM, который не зависит от реального DOM.Кто-нибудь знает почему?

Спасибо

https://jsfiddle.net/s8tkLeqp/

html

<!DOCTYPE html>
<html>
    <head>
        <title>Title of the document</title>
        <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.8/vue.min.js"></script>
    </head>
    <body>
        <template id="ddct-treeview-template">
            <dd-treeview-inner v-bind:data="data" v-bind:root="0" v-on:change="change"></dd-treeview-inner>
        </template>

        <template id="ddct-treeview-inner-template">
            <div :style="{'marginLeft':root>0 || data.collapsible ? '30px' : '0', 'marginBottom' : '4px'}">
                <span v-if="data.collapsible" @click="clickToggle" :class="['ddct_toggle', {'ddct_collapse_toggle' : !data.expanded}]"></span>
                <input type="checkbox" v-model="data.checked" v-indeterminate="data.indeterminate"> <span>{{data.name}}</span>
                <div :class="data.class" v-if="data.expanded">
                    <dd-treeview-inner v-for="child in data.children" v-bind:data="child" v-bind:root="root+1" v-on:change="propUp"></dd-treeview-inner>
                </div>
            </div>
        </template>

        <div id="app">
            <dd-treeview v-bind:data="data" v-model="treeVals"></dd-treeview>
            <div v-for="val in treeVals">{{val}}</div>
        </div>

    </body>
</html>

js

Vue.component('dd-treeview-inner', {
  template : $("#ddct-treeview-inner-template")[0],
  props: ['data', 'root'],
  data : function() {
    return {};
  },
  directives: {
    indeterminate: function(el, binding) {
      el.indeterminate = Boolean(binding.value);
    }
  },
  watch : {
    'data.checked' : function(new_val, old_val) {
      if (this.data.value) {
        this.updateTree(new_val);
      }
    }
  },
  methods : {
    clickToggle : function() {
      this.data.expanded = !this.data.expanded;
    },
    updateTree : function(state) {
      this.data.indeterminate = false;
      this.propDown(state);
      this.$emit('change');
    },
    propDown : function(state) {
      this.data.children.map(function(child) {
        child.checked = state;
        child.indeterminate = false;
        child.propDown(state);
      });
    },
    propUp : function() {
      var children = this.data.children;
      var checked = 0
      var indeterminate = 0;
      children.map(function(child) {
        if (child.checked && !child.indeterminate) checked++;
        if (child.indeterminate) indeterminate++;
      });
      if (indeterminate > 0) {
        this.data.checked = true;
        this.data.indeterminate = true;
      } else if (checked == 0) {
        this.data.checked = false;
        this.data.indeterminate = false;
      } else if (checked == children.length) {
        this.data.checked = true;
        this.data.indeterminate = false;
      } else {
        this.data.checked = true;
        this.data.indeterminate = true;
      }
      this.$emit('change');
    }
  }
});

Vue.component('dd-treeview', {
  template : $("#ddct-treeview-template")[0],
  props: ['value', 'data'],
  watch : {
    value : function(new_val, old_val) {
      this.setValues(new_val);
    }
  },
  data : function() {
    return {};
  },
  mounted : function() {
    this.setValues(this.value);
  },
  methods : {
    setValues : function(values) {
      values = values.map(x => x.toLowerCase());
      function ff(node) {
        if (node.value) {
          node.checked = values.indexOf(node.value.toLowerCase()) != -1;
          node.indeterminate = false;
        }
        node.children.map(ff);
      }
      ff(this.data); 
    },
    change : function() {
      var arr = [];
      function ff(node) {
        if (node.value && node.checked && !node.indeterminate) {
          arr.push(node.value);
        }
        node.children.map(ff);
      }
      ff(this.data);
      this.$emit('input', arr);
    }
  }
});

new Vue({
  el: $("#app")[0],
  data : {
    treeVals : ["B2"],
    data : {
      name : "ROOT",
      collapsible : true,
      expanded : true,
      checked:false,
      indeterminate:false,
      children : [
        {
          name : "A",
          collapsible : true,
          expanded : false,
          checked:false,
          indeterminate:false,
          children : [
            {
              name : "A1",
              children : [],
              checked:false,
              indeterminate:false,
              value : "A1"
            },
            {
              name : "A2",
              children : [],
              checked:false,
              indeterminate:false,
              value : "A2"
            }
          ]
        },
        {
          name : "B",
          collapsible : true,
          expanded : false,
          checked:false,
          indeterminate:false,
          children : [
            {
              name : "B1",
              children : [],
              checked:false,
              indeterminate:false,
              value : "B1"
            },
            {
              name : "B2",
              children : [],
              checked:false,
              indeterminate:false,
              value : "B2"
            }
          ]
        }
      ]
    }
  }
});

css

.ddct_toggle {
  position: relative;
}

.ddct_toggle::before {
  content: '+';
  display: block;
  position: absolute;
  top: 0;
  left: -30px;
  border: 1px solid red;
  border-radius: 50%;
  width: 24px;
  text-align: center;
  transition: all 0.4s;
  cursor: pointer;
}

.ddct_toggle.ddct_collapse_toggle::before {
  content: '-';
}

1 Ответ

1 голос
/ 14 марта 2019

Я полагаю, потому что вашему запущенному методу updateTree нужен смонтированный компонент.Так что в настоящее время на самом деле нет ничего наблюдающего за проверенным свойством B2Поэтому ничего не срабатывает.

Одна вещь, которую вы можете сделать, чтобы по крайней мере вызвать переключение:

watch : {
 'data.checked' :{
      handler: function(new_val, old_val) {
      if (this.data.value) {
        this.updateTree(new_val);
      }
    },
    immediate: true // this is equal to call of handler in mounted
  }
},

И еще одна вещь - просто загрузить пустые компоненты:

Vue.component('dd-treeview-inner', {
  template: $("#ddct-treeview-inner-template")[0],
  props: ['data', 'root', 'expanded'], // pass data.expanded
  data: function() {
    return {};
  },
  directives: {
    indeterminate: function(el, binding) {
      el.indeterminate = Boolean(binding.value);
    }
  },
  watch: {
    'data.checked': {
      handler: function(new_val, old_val) {
        if (this.data.value) {
          this.updateTree(new_val);
        }
      },
      /* immediate: true */
    }
  },
  methods: {
    clickToggle: function() {
      this.data.expanded = !this.data.expanded;
    },
    updateTree: function(state) {
      this.data.indeterminate = false;
      this.propDown(state);
      this.$emit('change');
    },
    propDown: function(state) {
      this.data.children.map(function(child) {
        child.checked = state;
        child.indeterminate = false;
        child.propDown(state);
      });
    },
    propUp: function() {
      var children = this.data.children;
      var checked = 0
      var indeterminate = 0;
      children.map(function(child) {
        if (child.checked && !child.indeterminate) checked++;
        if (child.indeterminate) indeterminate++;
      });
      if (indeterminate > 0) {
        this.data.checked = true;
        this.data.indeterminate = true;
      } else if (checked == 0) {
        this.data.checked = false;
        this.data.indeterminate = false;
      } else if (checked == children.length) {
        this.data.checked = true;
        this.data.indeterminate = false;
      } else {
        this.data.checked = true;
        this.data.indeterminate = true;
      }
      this.$emit('change');
    }
  }
});

Vue.component('dd-treeview', {
  template: $("#ddct-treeview-template")[0],
  props: ['value', 'data'],
  watch: {
    value: function(new_val, old_val) {
      this.setValues(new_val);
    }
  },
  data: function() {
    return {};
  },
  mounted: function() {
    this.setValues(this.value);
  },
  methods: {
    setValues: function(values) {
      values = values.map(x => x.toLowerCase());

      function ff(node) {
        if (node.value) {
          node.checked = values.indexOf(node.value.toLowerCase()) != -1;
          node.indeterminate = false;
        }
        node.children.map(ff);
      }
      ff(this.data);
    },
    change: function() {
      var arr = [];

      function ff(node) {
        if (node.value && node.checked && !node.indeterminate) {
          arr.push(node.value);
        }
        node.children.map(ff);
      }
      ff(this.data);
      this.$emit('input', arr);
    }
  }
});

new Vue({
  el: $("#app")[0],
  data: {
    treeVals: ["B2"],
    data: {
      name: "ROOT",
      collapsible: true,
      expanded: true,
      checked: false,
      indeterminate: false,
      children: [{
          name: "A",
          collapsible: true,
          expanded: false,
          checked: false,
          indeterminate: false,
          children: [{
              name: "A1",
              children: [],
              checked: false,
              indeterminate: false,
              value: "A1"
            },
            {
              name: "A2",
              children: [],
              checked: false,
              indeterminate: false,
              value: "A2"
            }
          ]
        },
        {
          name: "B",
          collapsible: true,
          expanded: false,
          checked: false,
          indeterminate: false,
          children: [{
              name: "B1",
              children: [],
              checked: false,
              indeterminate: false,
              value: "B1"
            },
            {
              name: "B2",
              children: [],
              checked: false,
              indeterminate: false,
              value: "B2"
            }
          ]
        }
      ]
    }
  }
});
.ddct_toggle {
  position: relative;
}

.ddct_toggle::before {
  content: '+';
  display: block;
  position: absolute;
  top: 0;
  left: -30px;
  border: 1px solid red;
  border-radius: 50%;
  width: 24px;
  text-align: center;
  transition: all 0.4s;
  cursor: pointer;
}

.ddct_toggle.ddct_collapse_toggle::before {
  content: '-';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html>

<head>
  <title>Title of the document</title>
  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

</head>

<body>
  <template id="ddct-treeview-template">
            <dd-treeview-inner :expanded="true" v-bind:data="data" v-bind:root="0" v-on:change="change"></dd-treeview-inner>
        </template>

  <template id="ddct-treeview-inner-template">
            <div v-if="expanded" :style="{'marginLeft':root>0 || data.collapsible ? '30px' : '0', 'marginBottom' : '4px'}">
                <span v-if="data.collapsible" @click="clickToggle" :class="['ddct_toggle', {'ddct_collapse_toggle' : !data.expanded}]"></span>
                <input type="checkbox" v-model="data.checked" v-indeterminate="data.indeterminate"> <span>{{data.name}}</span>
                <div :class="data.class">
                    <dd-treeview-inner :expanded="data.expanded" v-for="child in data.children" v-bind:data="child" v-bind:root="root+1" v-on:change="propUp"></dd-treeview-inner>
                </div>
            </div>
            <div v-else>
            <dd-treeview-inner :expanded="data.expanded" v-for="child in data.children" v-bind:data="child" v-bind:root="root+1" v-on:change="propUp"></dd-treeview-inner>
            </div>
        </template>

  <div id="app">
    <dd-treeview v-bind:data="data" v-model="treeVals"></dd-treeview>
    <div v-for="val in treeVals">{{val}}</div>
  </div>

</body>

</html>
...