VueJS emitted event not getting picked up by parent component - vue.js

I am struggling to understand why an emitted event on a child component is not getting picked up by the parent.
The event is getting emitted from the child component - I can see it in the toolbar along with the correct values. The method that is tied to the event is not getting called on the parent component. ChildComponent.vue is a form that is imported into ParentComponent.vue.
Here is what I have (that's working but not).
ParentComponent.vue
<child-component/>
<div v-show="this.label === 'acme'" #label:name="handleLabelName">
<h3>Hello World</h3>
...
</div>
...
methods: {
handleLabelName(name) {
console.log('handleManualType called'); // never getting here
console.log('name: ', name); // never getting here
}
},
ChildComponent.vue
...
<button data-label="acme" #click="handleClick($event)">Click Me</button>
...
methods: {
handleClick(event) {
const label = event.target.dataset.label;
this.$emit('label:name', label); // acme
},
The event is getting broadcast, but handleLabelName is never getting called. I've read through this great article many times and I believe I'm following it correctly. Is there something I am doing wrong to make handleLabelName never get called? I've also tried wrapping everything in a div like so:
<div #label:name="handleLabelName">
<div v-show="this.label === 'acme'">
<h3>Hello World</h3>
...
</div>
</div>
And still the same result. handleLabelName never gets called. I've also tried changing the event name/method to simple things like, foo and still no difference.
Thank you for any suggestions!

You are listening for events on a div instead of listening to your child component. You must put your listener on the DOM element that sends the event. Try something like this :
<child-component #my-event-name="handleLabelName"/>
Another point is that you might prefer using kebab case to name your custom events. It might be a problem to name your event with this character ":" (see https://v2.vuejs.org/v2/guide/components-custom-events.html)

Related

Vue $emit fires but parent component method does not trigger

I've been trying to pass data from child to parent. I'm not quite sure why what I've got right now isn't working. I should mention that the emitter gets fired but the method in the parent component doesn't seem to get called.
Children.vue:
<div #click="removeCommercials()" class="">
<u-button icon color="transparent">
<u-icon icon="switch-on" color="white"/>
</u-button>
</div>
methods: {
removeCommercials () {
this.$emit('test',{message:'HELLOOOO???'})
console.log("AAA")
}
},
Parent.vue:
<Children class="mt-4" #test="noCommercials"/>
methods: {
noCommercials(deactivate) {
console.log("?????")
this.deactivateCommercials = deactivate.message
console.log("REMOVEING COMMERCIALS")
console.log(this.deactivateCommercials)
},
},
From the console I can see the text AAA. From the vue developer tools I can also see the emitter getting fired with the correct data. But the method noCommercials in the parent component doesn't seem to trigger. Nothing from the method gets printed in the console.
From some other posts I've seen people talking about using this.$parent.$emit instead of this.$emit but that didn't do the trick for me. I've also seen others having the same problem but that's because they used the wrong child component.
I'm really confused as to why the noCommercials method does not trigger at all in the parent component. What am I doing wrong?
ADDITIONAL INFO:
I tried to trigger the same method from a different child component with $emit and it works. Now I just need to figure out why this particular child component is giving me issues.

How can I update data object whenever changes happens inside v-for of child

How can I update data object of parent whenever changes happen inside v-for. I have a child component that I use inside parent component.
ParentComponent.vue
<template>
....
....
<child-component
v-for="i in count"
ref="childComponent"
:key="i"
currentPage="i" // currentPage doesn't update.
:page="i"
/>
<q-header>
{{currentPage}} // always displays default value:1
</q-header>
</template>
<script>
data () {
return {
pageCount: 10,
currentPage: 1,
}
},
How can I update currentPage of data object whenever i changes inside v-for. I have tried using watch without much luck. I don't have access to child component and can't modify it.
Much appreciated!
There is some slight confusion with how v-for is working on the child-component here. Writing currentPage="i" as a property (which should actually be v-bind:currentPage in order for the i to be interpreted as JS) will simply declare the attribute on each child-component
How can I update currentPage of data object whenever i changes inside v-for
i doesn't "change" in the traditional context of running a for loop inside of a normal JavaScript application. In Vue, your rendering logic and application logic are separate, and rightly so, because running logic as part of the rendering doesn't really make sense.
For example, let's look at how your app will render the child-component:
<!-- Vue output -->
<child-component ... currentPage="1" />
<child-component ... currentPage="2" />
<child-component ... currentPage="3" />
So let's look at separating the rendering logic from the application logic.
I realise you don't have access to child-component, but based on the context I will assume it is some kind of tabbing functionality (based on you trying to set a value for the "current page" - feel free to be more specific and I can update my answer).
We need to bridge that gap between the rendering logic and the application logic and we can do that by using events:
<child-component
v-for="i in count"
:ref="`childComponent-${i}`" // ref should be unique so add the i as part of it
:key="i"
:page="i"
v-on:click="currentPage = i" // when the user clicks this child component, the current page will be updated
/>
You may have to utilise a different event other than click but I hope this gets you closer to what you are trying to achieve. For the value of currentPage to update there has to be some kind of user input, so just find out which event makes the most sense. Maybe the child-component library you are using has custom events that are more appropriate.
you should look into Custom Events.
https://v2.vuejs.org/v2/guide/components-custom-events.html
Idea is, that whenever there is some update of your desire in child component, you can execute this.$emit(“change”), which will throw an event.
On parent side you can catch this event by #change=“myMethod” as one of the attributes.
methods: {
myMethod() {
console.log("Testing")
}
}
<child-component
v-for="i in count"
ref="childComponent"
:key="i"
currentPage="i"
:page="i"
#change=“myMethod”
/>
Let me know if that helped.

How to emit data to DIRECT PARENT only in Vue 2.0?

I have a reusable child component SizeSelector and it has been used under 2 components served for different purposes.
In SizeSelector
onSizeSelectionChanged() {
if (this.isMultiSelectable === false) {
this.selectedVariants = [this.selectedVariant];
}
this.$emit('update', this.selectedVariants);
}
Under component A:
<product-size-form :product="product" #update="selectedVariant = $event[0]"></product-size-form>
Under component B:
<product-size-form
:product="product"
:is-multi-selectable="true"
#update="selectedVariants = $event; log($event, 'modal')">
</product-size-form>
My problem is when I click and change the selectedVariants in B it emited to A. How to fix it? I seems the event got emitted globally
Update
It sounds crazy and it is.
This one is not working
<div class="product-form">
<product-size-form
:product="product"
:is-multi-selectable="true"
#update="selectedVariants = $event; log($event, 'modal')">
</product-size-form>
</div>
And this one works completely fine
<div class="product-form-wow-this-is-working-wtf">
<product-size-form
:product="product"
:is-multi-selectable="true"
#update="selectedVariants = $event; log($event, 'modal')">
</product-size-form>
</div>
The way I see it you have 2 options:
1)Just change the names of your events, call one "update" (not particularly descriptive), and the other "update2" or anything except "update".
2)The other thing you can do is work react style i.e. instead of emitting an event pass in a prop with a callback function that does something in the parent.
Like it was pointed out this should not be happeing events are not supposed to be emitted globally.

How to refactor repetitive attributes in Vue.js

Suppose I have a form and many fields in it. I want to subscribe to change for each form field. I will have to add #change="doSome" for every field. If I have many fields it gets somewhat repetitive. How do I refactor it?
You can listen for the change event on the form tag itself instead of listening on the individual inputs.
<form #change="doSomething"> will run the function doSomething() when something inside the form has changed eg: if you type in an input and release focus
In the doSomething function, you want to find out what element changed. We get this info from the event parameter provided from the input event:
methods: {
doSomething(event) {
this.lastEvent = event.target.value;
}
}
You can see this in effect on this Codepen example
If the form element is a child of an element inside a component like so:
<template>
<div>
<form></form>
</div>
</template>
the #changeevent-listener will not work as there is nothing that changes on the root element (div) on the component.
In this case, we need to add the .native modifier, like so: #change.native="doSomething".

When does binding to ref attribute become valid in Aurelia?

This is a follow up to this question: Access a DOM element in Aurelia
Is there a hook in the Screen Activation Lifecycle which allows me to run code after ref bindings have been set up? Currently it seems like there is a period of time after the activate hook is called when the ref bindings are not set up yet and then at some point they get activated. I tested this by adding a <div ref="myDiv"></div> to near the bottom of welcome.html in a cloned version of the latest (v0.13.0) skeleton-navigation repo and testing the existence of the reference in the view-model like this:
export class Welcome{
heading = 'Welcome to the Aurelia Navigation App!';
firstName = 'John';
lastName = 'Doe';
testMyDiv() {
console.log("Getting my div")
console.log(this.myDiv)
}
get fullName(){
this.testMyDiv()
return `${this.firstName} ${this.lastName}`;
}
welcome(){
alert(`Welcome, ${this.fullName}!`);
}
}
A snippet of the bottom of the template...
<button type="submit" class="btn btn-default">Submit</button>
</form>
<div ref="myDiv"></div>
</section>
</template>
This is a snapshot of what I see in the console...
welcome.js:10 Getting my div
welcome.js:11 undefined
welcome.js:10 Getting my div
welcome.js:11 undefined
welcome.js:10 Getting my div
welcome.js:11 <div ref=​"myDiv" class=​"au-target">​</div>​
welcome.js:10 Getting my div
welcome.js:11 <div ref=​"myDiv" class=​"au-target">​</div>​
(continues)
The print outs like this goes on indefinitely. You can see that fullName() is being called regularly to update the screen if the name changes (I assume this is the dirty checking)... but you can see that at the beginning there is a period when the referenced div is NOT valid as a property of the view-model, and then it IS valid. Can someone explain this? Is there a way to hook into the view-model after the ref becomes valid?
In general, bindings are processed and available after the bind callback. However, in this case since you need to access the DOM element, you will need the ViewModel to be bound and attached to the view, so use the attached callback.
class ViewModel {
bind() {
this.refItem == undefined; // true
}
attached() {
this.refItem == undefined; // false
}
}
As you noted in the comments, more information on the activator callbacks is available here: http://aurelia.io/docs.html#extending-html