Vue2.0 parent component cannot listen to $emit event in child component by $on

Here is the code.Is there anything wrong?I used $on to listen to event $emit by child component, but it doesn’t work.It works if I use v-on:increment on the <button-counter>

I have found the answer,vm.$on can only listen event $emit by itself. I have to use v-on,if I want to listen to event $emit by child component

You can see example to understand emit and listen in Vue 2

Emit and listen in Vue 2

1 Like

I will describe my problem here, it’s closely related. The above example is I think really straightfwd. This is basically that the component creates a custom event and in the template you bind the event with a parent instance’s method.

My problem is when I use dynamic components, I will not define a tag in my HTML template, it will just be <component></component>. So I cannot bind the component’s event to an instance’s method…
I have to hardcode the instance.

My component’s template:

<a v-on:click.prevent="loadA" class="btn" href="#">Load "A" Component</a>
...

Where it will dynamically be mounted:

<section id="my-app">

  <component v-bind:is="viewThisComp"></component>

</section>

So as “my-comp” will be dynamically bound, I cannot do the following with it:

<component v-bind:is="viewThisComp">   
    <my-comp @viewthiscomp="loadOtherComponent"></my-comp>
</component>

How to solve this problem without hard-wiring my root/parent instance into my-comp’s “loadA” method?! Because just:

this.$emit('loadA', 'A')

won’t work. What work’s in the component’s method is:

this.$parent.$emit('loadA', 'A')

And then you need to catch that with your instance. Hard-coded into the logic.

vm.$on('loadA', function (selected) {
    // Now I can reach my vm instance's methods and I will get the emitted event.
});

Any tip, advice on that?

Updated my reply with a fiddle:

https://jsfiddle.net/sasiszabi/1s4vtk6k/15/

2 Likes

https://jsfiddle.net/1s4vtk6k/20/
It works

2 Likes

Yes, thanks!

Back to original question: what if children passed via slot? How I should listen to events from them? Example:

<parent>
  <slot/>
</parent>

// usage

<parent>
	<child/>
	<child/>
	<child/>
</parent>	

Am I understand it correctly that there is no way to listen event from child inside parent? Except only with global vue event bus. Calling from child $this.parent.emit(‘evt’) smells the same bad as calling parent methods.

parent should provid a scoped slot instead of a normal one. then you can do this;

<parent>
  <template slot-scope="{ handlerMethod }">
    <child @click="handlerMethod"/>
    <child @click="handlerMethod"/>
    <child @click="handlerMethod"/>
  </template>
</parent>

In parent:

<slot v-bind:handlerMethod="handlerMethod" > </slot>
<!-- v-bind, not v-on !!-->

Hi

Can you explain this a little bit more to me please. I don’t really get it

I have a modal component like this:

Child: (modal)

<template>
    <section class="modal">
        <section class="modal-overlay" @click="close"></section>
        <section class="modal-content">
            <slot></slot>
        </section>
    </section>
</template>

And when i want to create a custom modal above that (modal with content) i do something like this:
Parent: (edit_user_modal)

<template>
	<modal>
		some content
		<input type="text">
		<button @click="close">close</button>
	</modal>
</template>

Now the close function in de edit_user_modal should trigger the function that is defined in the child (modal). How should I do this?

This would inded be done with a scoped slot (read the guide about it here)

<template>
    <section class="modal">
        <section class="modal-overlay" @click="close"></section>
        <section class="modal-content">
            <!-- you pass the close method as a prop to the slot -->
            <slot :close="close"></slot>
        </section>
    </section>
</template>
<template>
  <modal>
   <!-- "props" is an opbject containing all props passed to this slot by <modal> -->
    <template slot-scope="props">
      some content
      <input type="text">
      <!-- so now we can pass the "close" prop as a callback method to the click event -->
      <button @click="props.close">close</button>
    </template>
  </modal>
</template>

It works like a charm. Only got one little problem now. It throws me a error:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders

Now I do know I should not change a prop directly but by doing an $emit when the value changes. So I did that. like so:

child: (modal)

close: function () {
    this.$emit('input', false);
},

And again in the parent: (edit_user_modal)

watch: {
	value: function () {
		this.$emit('input', this.value);
	},
}

But like i said, it throws the error. I am not directly changing the prop value, right? Am i not understanding something correctly or am i missing something?.

If you need any more information/more code, let me know.

Thanks for helping me out!

props property is only for one way data binding , that’s why you get error about mutating.