2

So I've recently started working with Vue js. I'm attempting to dynamically add and remove Vue nodes. It's a bit difficult to describe the issue so I've created a demo to illustrate it.

Vue.component('context', {
  data() {
    return {
      test: '<context></context>', //Dummy recursive data to illustrate issue
      child: ''
    }
  },
  methods: {
    addChild() {
      this.child = this.test
    },
    removeChild() {
      this.child = ''
    }
  },
  computed: {
    dynamic() {
      return Vue.compile(this.child)
    },
    style() {
      return {
        'background-color': '#' + randHex(6)
      }
    }
  },
  template: `
        <div :style="style" @click="addChild" @click.shift="removeChild">
            <component :is="dynamic"></component>
        </div>
    `
})

new Vue({
  el: '#app'
})




function randHex(digits) {
  let hex = Math.floor(Math.random() * Math.pow(16, digits)).toString(16)
  return '0'.repeat(digits - hex.length) + hex
}
html,
body {
  height: 100%;
  overflow: hidden;
}

div {
  width: 90%;
  height: 90%;
}
<script src="https://unpkg.com/vue@2.4.3/dist/vue.js"></script>
<p>Click on the block to add, Shift-Click ro remove. Why does shift clicking always remove all inner blocks and not just the ones as children to the shift clicked block?</p>

<div id="app">
  <context></context>
</div>

Above you will see that Clicking on the colored rectangles adds a inner child as intended. However when you shift click on a rectangle it not only removes its children, but ALL children! (Even ones that are parents to the current node.)

Initially I had thought the click event was "bleeding through" to the lower elements, I did however create a bit more complex test that offset the elements position to not be above one another, this still produced the same strange behavior.

Any help on understanding / resolving this issue would be greatly appreciated.

Hex Crown
  • 753
  • 9
  • 22

1 Answers1

2

The issue is event bubbling: while the target child receives the click and removes its children, as you intended, the event bubbles up to all the parents who do the same in turn as well.
Luckily, Vue offers easy access to event modifiers, namely .stop will stop the event from propagating any further.

Also, it seems you're making this unnecessarily complex, unless I'm missing a requirement you haven't outlined in the question? You don't need to recompile the child component and you don't need a computed property.

I've updated your code example with the above issues in mind:

Vue.component('context', {
  data() {
    return {
      hasChild: false,
      style: {
        'background-color': '#' + randHex(6)
      }
    }
  },
  methods: {
    addChild() {
      this.hasChild = true
    },
    removeChild() {
      this.hasChild = false
    }
  },
  template: `
        <div :style="style" @click="addChild" @click.shift.stop="removeChild">
            <context v-if="hasChild"></context>
        </div>
    `
})

new Vue({
  el: '#app'
})

function randHex(digits) {
  let hex = Math.floor(Math.random() * Math.pow(16, digits)).toString(16)
  return '0'.repeat(digits - hex.length) + hex
}
html,
body {
  height: 100%;
  overflow: hidden;
}

div {
  width: 90%;
  height: 90%;
}
<script src="https://unpkg.com/vue@2.4.3/dist/vue.js"></script>

<div id="app">
  <context></context>
</div>
tony19
  • 125,647
  • 18
  • 229
  • 307
Etheryte
  • 24,589
  • 11
  • 71
  • 116
  • This doesn't delete any child anymore.. only if you click outside the child – CodeHacker Sep 13 '17 at 10:12
  • So the .stop fixes the issue. Thanks for that, I'll mark this as the answer, but to clarify my reasoning behind using this aproach is that a server will be serving "pages" that are actually just blocks of html with components used throughout, so basically "context" in this case is acting as sort of an iFrame in a way. Because i don't know what "page" is coming from the server, only what components can possibly be on the page but not what arrangement they are in I was thinking using this would be the best bet? – Hex Crown Sep 13 '17 at 10:22
  • @HexCrown In that case, yes, it's probably easier if the templates are compiled manually. – Etheryte Sep 13 '17 at 11:29
  • @CodeHacker I think you might have misunderstood the initial problem statement. As far as I can tell that's the intended behavior: when you shift-click in a parent node, its children are removed. – Etheryte Sep 13 '17 at 11:34