title: How to fix vue updates with checkboxes?
body: <p>I'm trying to make a tree view with checkboxes where the parent is always in the correct state based on it's children. Including indeterminate values. And v-model it's values into an array. This is what I got so far</p>
  
  <p>The problem is I set B2 (leaf node) to be selected, but then I expect it's parent and it's parent to become indeterminate. However it doesn't. I believe this is because I use <code>v-if</code> (where I have <code>v-if="data.expanded"</code>) and not <code>v-show</code> in the treeview children, and I have the parent of B2 collapsed, so it doesn't show.</p>
  
  <p>But I don't understand why this is a problem because i'm only making changes to the data model and doing emit events in a virtual dom which isn't relying on the actual DOM. Does anyone know why?</p>
  
  <p>Thanks</p>
  
  <p><a href="https://jsfiddle.net/s8tkLeqp/" rel="nofollow noreferrer">https://jsfiddle.net/s8tkLeqp/</a></p>
  
  <p>html</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
      &lt;head&gt;
          &lt;title&gt;Title of the document&lt;/title&gt;
          &lt;script src="https://code.jquery.com/jquery-3.3.1.min.js"&gt;&lt;/script&gt;
          &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.8/vue.min.js"&gt;&lt;/script&gt;
      &lt;/head&gt;
      &lt;body&gt;
          &lt;template id="ddct-treeview-template"&gt;
              &lt;dd-treeview-inner v-bind:data="data" v-bind:root="0" v-on:change="change"&gt;&lt;/dd-treeview-inner&gt;
          &lt;/template&gt;
  
          &lt;template id="ddct-treeview-inner-template"&gt;
              &lt;div :style="{'marginLeft':root&gt;0 || data.collapsible ? '30px' : '0', 'marginBottom' : '4px'}"&gt;
                  &lt;span v-if="data.collapsible" @click="clickToggle" :class="['ddct_toggle', {'ddct_collapse_toggle' : !data.expanded}]"&gt;&lt;/span&gt;
                  &lt;input type="checkbox" v-model="data.checked" v-indeterminate="data.indeterminate"&gt; &lt;span&gt;{{data.name}}&lt;/span&gt;
                  &lt;div :class="data.class" v-if="data.expanded"&gt;
                      &lt;dd-treeview-inner v-for="child in data.children" v-bind:data="child" v-bind:root="root+1" v-on:change="propUp"&gt;&lt;/dd-treeview-inner&gt;
                  &lt;/div&gt;
              &lt;/div&gt;
          &lt;/template&gt;
  
          &lt;div id="app"&gt;
              &lt;dd-treeview v-bind:data="data" v-model="treeVals"&gt;&lt;/dd-treeview&gt;
              &lt;div v-for="val in treeVals"&gt;{{val}}&lt;/div&gt;
          &lt;/div&gt;
  
      &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>js</p>
  
  <pre><code>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 &amp;&amp; !child.indeterminate) checked++;
          if (child.indeterminate) indeterminate++;
        });
        if (indeterminate &gt; 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 =&gt; 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 &amp;&amp; node.checked &amp;&amp; !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"
              }
            ]
          }
        ]
      }
    }
  });
  </code></pre>
  
  <p>css</p>
  
  <pre><code>.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: '-';
  }
  </code></pre>