vue.jsparentchildrenemit

VueJs Tree recursive elements emits to parent


How do you emit event inside recursive child components vuejs

Taking the tree example from vue site https://v2.vuejs.org/v2/examples/tree-view.html

How would you transmit on click to the parent each clicked elements id?


Solution

  • In the case of recursive elements, you can create an event bus in the parent, pass it to the children as a prop, and have each prop pass it to any children they generate.

    Each child emits events on the bus, and the parent handles them. I copied the tree-view exercise you linked and added the bus functionality.

    // demo data
    var data = {
      name: 'My Tree',
      children: [{
          name: 'hello'
        },
        {
          name: 'wat'
        },
        {
          name: 'child folder',
          children: [{
              name: 'child folder',
              children: [{
                  name: 'hello'
                },
                {
                  name: 'wat'
                }
              ]
            },
            {
              name: 'hello'
            },
            {
              name: 'wat'
            },
            {
              name: 'child folder',
              children: [{
                  name: 'hello'
                },
                {
                  name: 'wat'
                }
              ]
            }
          ]
        }
      ]
    };
    
    var itemId = 0;
    
    // define the item component
    Vue.component('item', {
      template: '#item-template',
      props: {
        model: Object,
        bus: Object
      },
      data: function() {
        return {
          open: false,
          id: ++itemId
        }
      },
      computed: {
        isFolder: function() {
          return this.model.children &&
            this.model.children.length
        }
      },
      methods: {
        toggle: function() {
          if (this.isFolder) {
            this.open = !this.open;
            this.bus.$emit('toggled', this.id);
          }
        },
        changeType: function() {
          if (!this.isFolder) {
            Vue.set(this.model, 'children', [])
            this.addChild()
            this.open = true
          }
        },
        addChild: function() {
          this.model.children.push({
            name: 'new stuff'
          })
        }
      }
    })
    
    // boot up the demo
    var demo = new Vue({
      el: '#demo',
      data: {
        treeData: data,
        bus: new Vue()
      },
      created() {
        this.bus.$on('toggled', (who) => {
          console.log("Toggled", who);
        });
      }
    })
    body {
      font-family: Menlo, Consolas, monospace;
      color: #444;
    }
    
    .item {
      cursor: pointer;
    }
    
    .bold {
      font-weight: bold;
    }
    
    ul {
      padding-left: 1em;
      line-height: 1.5em;
      list-style-type: dot;
    }
    <script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
    <!-- item template -->
    <script type="text/x-template" id="item-template">
      <li>
        <div :class="{bold: isFolder}" @click="toggle" @dblclick="changeType">
          {{model.name}}
          <span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
        </div>
        <ul v-show="open" v-if="isFolder">
          <item class="item" :bus="bus" v-for="model in model.children" :model="model">
          </item>
          <li class="add" @click="addChild">+</li>
        </ul>
      </li>
    </script>
    
    <p>(You can double click on an item to turn it into a folder.)</p>
    
    <!-- the demo root element -->
    <ul id="demo">
      <item class="item" :bus="bus" :model="treeData">
      </item>
    </ul>