Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the event source from custom events in Vue?

I'm building a Vue component that consists of an unspecified number of child components. Only one child component is visible at all times, and the user can only switch between child components when the one currently visible has emitted an is-valid event.

I want to keep this decoupled, such that children do not know about their parent and only communicate by emitting events. This also means that the children do not know their position within the parent component.

So, the parent component somehow has to keep track of which child the event came from. If the event came from the right child (the one currently visible) then some buttons are activated that allows the user to go to the next or previous child.

Here's my code so far:

HTML

<div id="app">
    <template id="m-child">
        <div>
            <button v-on:click="setstate(true)">Valid</button>
            <button v-on:click="setstate(false)">Invalid</button>
        </div>
    </template>

    <template id="m-parent">
        <div>
            <m-child v-on:newstate="newchildstate"></m-child>
            <m-child v-on:newstate="newchildstate"></m-child>
            <m-child v-on:newstate="newchildstate"></m-child>
        </div>
    </template>

    <m-parent></m-parent>
</div>

JS

Vue.component('m-child', {
    template: '#m-child',
    data: function() {
        return {};
    },
    methods: {
        setstate: function (valid) {
            this.$emit('newstate', valid);
        }
    }
});

Vue.component('m-parent', {
    template: '#m-parent',
    methods: {
        newchildstate: function (valid) {
            console.log('valid:' + valid + ', but where from?');
        }
    }
});

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

Of course I could hardcode an index on the child event binding:

        <m-child v-on:newstate="newchildstate(0, $event)"></m-child>
        <m-child v-on:newstate="newchildstate(1, $event)"></m-child>
        <m-child v-on:newstate="newchildstate(2, $event)"></m-child>

But that would make the whole setup a lot less modular, I just want to be able to plug in a number of children in the DOM and make it work right away.

I've looked at the API for Vue events and there doesn't seem to be a way to get the source from the event object.

like image 266
soren.qvist Avatar asked Oct 29 '22 13:10

soren.qvist


1 Answers

This depends on what you want to receive back, my personal preference is to pass in a prop to set a unique id and pass it back in the $emit:

<m-child v-on:newstate="newchildstate" :id="1"></m-child>
<m-child v-on:newstate="newchildstate" :id="2"></m-child>
<m-child v-on:newstate="newchildstate" :id="3"></m-child>

Then in your child component you can emit an object with the state and id the id:

Child:

this.$emit('newstate', {id: this.id, state: valid});

Parent:

newchildstate: function (valid) {
  console.log('valid:' + valid.state + ', from' + valid.id);
}

I realise that this doesn't look hugely different from your hard coded example, but at some point your parent is going to want to deal with the event, so you could set up an array in data with the initial states and then use a v-for:

data: {
  children: [true, false, false] // setup states
}

You would then do:

<div v-for="(state, index) in states">
  <m-child v-on:newstate="newchildstate" :id="index"></m-child>
</div>

And in your view model:

  methods: {
    newchildstate: function(valid) {
      this.$set(this.states, valid.id, valid.state);
    }
  }

Here's a JSFiddle that initiates the array dynamically via a prop and sets up the child components: https://jsfiddle.net/2y9727e2/

like image 151
craig_h Avatar answered Nov 15 '22 05:11

craig_h