Search code examples
javascriptvue.jseventscomponentsemit

Vue.js : Method not firing in parent component after emit


A child component emits an event on input change with a payload:

Vue.component('gum-option', {
    inheritAttrs: false,
    props: ['label'],
    template: `
        <label class="gum-option">
            <input 
                type="radio"
                v-bind="$attrs"
                v-on:change="$emit('update', this.label)">
            <span>{{ label }}</span>
        </label>
    `
});  

Its parent component listens to this event in order to fire a method that throws an alert:

Vue.component('gum-select', {
    template: `
        <div v-on:update="updateValue"> 
            <label>{{label}}</label>
            <div class="selected">
                {{selectedOption}}
            </div>
            <slot v-bind="options"></slot>
        </div>
    `,
    data: function () {
        return {
            selectedOption: ''
        }
    },
    props:['options', 'name', 'label'],
    methods: {
        updateValue: function(event) { // method invoked by the "update" event
            alert(event); // Not firing ! Why
        }
    }
});


You can find the bug on this pen: https://codepen.io/bourpie/pen/JjXdjzg?editors=1011


Solution

  • The listener should be put on the gum-option component in App, as well as the method updateValue. There were some other mistakes so this is the fixed demo:

    <div id="myApp">
      <gum-select name="options" label="Options" v-bind:options="options" >
        <gum-option 
              v-for="option in options" 
              v-bind:key="option.value" 
              v-bind:value="option.value" 
              v-bind:label="option.texte" 
              name="province"
              v-on:update="updateValue">
        </gum-option>
      </gum-select>
    </div>
    
    Vue.component('gum-option', {
        inheritAttrs: false,
        props: ['label'],
        template: `
            <label class="gum-option">
                <input 
                    type="radio"
                    v-bind="$attrs"
                    v-on:change="$emit('update', label)">
                <span>{{ label }}</span>
            </label>
        `
    }); 
    
    Vue.component('gum-select', {
        template: `
            <div> 
                <label>{{label}}</label>
                <div class="selected">
                    {{selectedOption}}
                </div>
                <slot v-bind="options"></slot>
            </div>
        `,
        data: function () {
            return {
                selectedOption: ''
            }
        },
        props:['options', 'name', 'label'],
    });
    
    new Vue({
        el: '#myApp',
        data: { 
            options: [
                {value: '1', texte: 'Option 1'},
                {value: '2', texte: 'Option 2'},
                {value: '3', texte: 'Option 3'}
            ]
        },
      methods: {
            updateValue: function(label) { 
                console.log(label);
            }
        }
    });