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?

5
  • This thread has what you're looking for (via props, a store, or Vuex). The Answer chosen was using props: stackoverflow.com/a/41923080/1695318 Commented Mar 13, 2017 at 17:23
  • Do you know what the children will be? As in do you know they are "child" components? Commented Mar 13, 2017 at 19:32
  • Yes @BertEvans , they will be child components. Commented Mar 13, 2017 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. Commented Mar 14, 2017 at 7:22
  • I did try props before propsData, and no props were being picked up in child components. See: stackoverflow.com/questions/42764535/… Commented Mar 14, 2017 at 8:53

1 Answer 1

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

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.
Thanks for taking the time to answer, it's greatly appreciated.
@Kiee Take a look at this, using scoped slots. codepen.io/Kradek/pen/KWvqRL/?editors=1010
I amended my comment as I solved the seperate scopedSlot issue and it's not technically relevant to initial question. Thanks again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.