Vue2 event bus working after second call

时间:2017-12-18 06:07:51

标签: javascript html5 vue.js event-handling vuejs2

I have a nested component, and the child component should receive a parameter from the main instance, but the problem is that I have to call the event twice the get the parameter.

index.html

<div id="app">
    <button @click="displayComponent">Display</button><br/><hr/>
    {{ message }}

    <mycomponent v-if="showComponent" @hide="hideComponents"></mycomponent>
</div>

code.js

window.bus = new Vue();

Vue.component('mycomponent', {
    template: `
        <div>
            <h3>Im the parent component</h3>
            <childcomponent></childcomponent>
            <button @click="$emit('hide')">Hide components</button>
        </div>
    `
});

Vue.component('childcomponent', {
    template:`
        <div>
            <h4>Im the child component</h4>
            <p>{{ data }}</p>
        </div>
    `,

    data() {
        return {
            text: 'Nothing loaded'
        };
    },

    methods: {
        test() {
            alert('hello');
        },

        getData(x) {
            this.text = x;
        }
    },

    created(){
        bus.$on('extraCall', data => {
            this.getData(data);
            this.test();
        });
    }
});

const app = new Vue({
    el: '#app',

    data: {
        message: 'hello world!',
        showComponent: false
    },

    methods: {
        displayComponent() {        
            bus.$emit('extraCall', 'this is some extra text');
            this.showComponent = true;
        },

        hideComponents() {
            this.showComponent=false;
        }
    }
});

The text value inside the child component element is set to a default value, after the Display button is clicked it fires bus.$emit with the extraCall event with some text as a parameter, this should update the text value, and it happens only after a second click to the Display button.

What am I missing?

1 个答案:

答案 0 :(得分:1)

<mycomponent> (and its child <childcomponent>) aren't instantiated at the time the Display button is clicked because of v-if="showComponent".

First click:

  1. extraCall is emitted on the bus, but there's no listeners for that event so it is ignored.
  2. <mycomponent> is instantiated after setting showComponent to true.
  3. <mycomponent> registers a listener for the extraCall event in its created hook.

Second click:

  1. extraCall is emitted on the bus and <mycomponent> handles it.

You might think that the bus.$emit() and this.showComponent = true lines should be swapped so that <mycomponent> gets instantiated before the event is emitted, but this still will not work because Vue defers the creation of the component until the next microtask when the view is updated.

This might work:

displayComponent() {
  this.showComponent = true;

  // Wait for child component to be instantiated
  this.$nextTick(() => {  
    bus.$emit('extraCall', 'this is some extra text');
  });
}

If the above code works for you, I still don't really recommend it though. You shouldn't need to account for the creation of the child component before emitting the event (it couples your components together). You should share the data some other way, check other SO questions on best ways to share data across components.