1

I have a parent component that wraps multiple child components. I want the parent component to manage the state of all its children. When the child is clicked I want the parent to know and I want to update all it's siblings so they know which one is active.

JSFIDDLE DEMO

Vue.component('child', {
  template: '#childtemplate',
  props: ['index', 'activeIndex'],
  methods: {
    updateActiveIndex: function() {
      console.log("emitting");
      this.$emit('updateEvent', this.index);
    }
  }
});

Vue.component('parent', {
  data: function() {
    return {
      activeIndex: 0
    }
  },
  render: function(createElement) {
    console.log("rendering ai->", this.activeIndex);

    this.$options._renderChildren.forEach(function(item, index) {
      if (item.data === undefined)
      return;

      item.componentOptions.propsData = {
        index: index,
        activeIndex: this.activeIndex
      }
    }.bind(this));

    return createElement('div', {}, this.$options._renderChildren);
  },
  methods: {
    handleToggle: function(index) {
      this.activeIndex = index;
    }
  },
  created: function() {
    this.$on('updateEvent', this.handleToggle);
    //try manually update it, but the children don't update.
    setTimeout(function(){this.activeIndex = 6}.bind(this), 3000);
  }
});

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

I have tried adding event listener option to the createElement function in parent render() like so:

return createElement('div', {on:{updateEvent: this.handleToggle}}, this.$options._renderChildren);

I have tried setting an $on listener in parent created function. But this is not triggered.

I have tried manually updating activeIndex with a timeout which updates it on the root, but children are not updated.

A hack solution and the only thing I've found to work is to reference a $parent callback directly from the child passing up an index, then in the parent looping through the children and assigning the prop manually. This causes vue warn errors but gets the job done.

Is there a better way?

Kiee
  • 10,661
  • 8
  • 31
  • 56
  • This thread has what you're looking for (via props, a store, or Vuex). The Answer chosen was using props: http://stackoverflow.com/a/41923080/1695318 – iamjpg Mar 13 '17 at 17:23
  • Do you know what the children will be? As in do you know they are "child" components? – Bert Mar 13 '17 at 19:32
  • Yes @BertEvans , they will be `child` components. – Kiee Mar 13 '17 at 19:35
  • propsData isn't reactive afaik. That's mostly used for unit testing. Use `props` instead. That will work interactively. If you still want to keep track of the state in every child on it's own, use a public `pubsub` approach. – Codebryo Mar 14 '17 at 07:22
  • I did try `props` before `propsData`, and no props were being picked up in `child` components. See: http://stackoverflow.com/questions/42764535/vuejs-pass-props-to-children-in-render-function-call – Kiee Mar 14 '17 at 08:53

1 Answers1

1

I'm not sure this is better, but it works in this modified fiddle. Basically it iterates over the default slot, discards anything that isn't a child, sets the appropriate properties and includes whatever is currently in each child's slot.

render: function(createElement) {
  console.log("rendering ai->", this.activeIndex);

  const children = [];
  for (let i=0; i < this.$slots.default.length; i++){
    if (!(this.$slots.default[i].tag == "child"))
      continue;

    children.push(createElement(Child, {
      props:{
        index: i,
        activeIndex: this.activeIndex
      },
      on:{
        updateEvent: this.handleToggle
      }
    }, this.$slots.default[i].children))

  }

  return createElement('div', {}, children);
}
Bert
  • 80,741
  • 17
  • 199
  • 164
  • You should use Vue the way it's intended to work and don't try to run over data manually again. Vue has reactivity in place, just leverage it correctly. – Codebryo Mar 14 '17 at 07:22
  • Thanks for taking the time to answer, it's greatly appreciated. – Kiee Mar 14 '17 at 12:00
  • @Kiee Take a look at this, using scoped slots. http://codepen.io/Kradek/pen/KWvqRL/?editors=1010 – Bert Mar 14 '17 at 20:10
  • I amended my comment as I solved the seperate scopedSlot issue and it's not technically relevant to initial question. Thanks again. – Kiee Mar 14 '17 at 20:47