Notify parent component as soon as event occurs in child event? - vue.js

I need that when a user clicks a button on the child component the parent component receives cart.lenght to be assigned the count property of an element.
Child component code
<q-btn flat round color="faded" icon="fas fa-shopping-cart" #click="cart(item.id)"/>
cart: function (Id) {
let cart = []
cart.push(Id)
this.$emit('totalItensCart')
this.$q.localStorage.set('cart', cart)
}
How do I display the cart.length value in the count property that is in the parent component?
Parent component code
<q-route-tab
count="5"
icon="fas fa-cart-plus"
to="/cart"
exact
slot="title"/>

As per the vue event documentation, we want to emit an event from the child component to the parent component. You are correct with the this.$emit method, but it can take two parameters in order to pass data, like so:
this.$emit("name", data);
So, let's emit a count of the number of items in the cart from the first child:
this.$emit("cartUpdate", cart.length);
Now we need to handle this in the parent. We'll first need a data property to keep track of the totalCartItems in the parent:
data: function () {
return {
totalCartItems: null
}
}
Assuming that both of your code snippets are in different children of the same parent, and that the first child (the one that emits cartUpdate) has the component name first-child:
<first-child #cartUpdate="cartUpdate"></first-child>
This will call the cartUpdate() method in the parent component whenever the child emits an event called cartUpdate (using the # shorthand for v-on). The method is very simple, only updating the totalCartItems data property in the parent:
cartUpdate: function (totalCartItems) {
this.totalCartItems = totalCartItems;
}
Finally, let's ensure this gets updated in the second child component by binding it to the data value:
<q-route-tab
:count="totalCartItems"
icon="fas fa-cart-plus"
to="/cart"
exact
slot="title"/>

Related

Vue 2. How get child value throuth v-model binding

I'm using v-model in custom component, but when I try to get the data(value props) of the child component in the parent component, then I get the old value, although the data in the parent has changed. If I get the data of the child component through setTimeout, then I get the data I need. How do I synchronize the v-model #input event and the data I receive from the child element in the parent?
This is the logic: there are AppForm(parent) component and AppSelect(child) component. I'm binding v-model on AddSelect, and I follow the changes through watch() { v-model-data }. Then v-model data has changed I call AppForm.data() method, which iterates through the list of children and gets the value prop of AppSelect component, but this value is old.
Short example:
https://codesandbox.io/s/serene-wildflower-5seqx?file=/src/App.vue
You're trying to get the child component's prop inside a watcher watching the data property that the prop is bounded to. Of course, the watcher triggers first, then the data is passed onto the child component and the prop is updated. Its not a good design if you are relying on the order that Vue events and functions are fired, but if you must, you can use vm.$nextTick() function to get values after everything is synced.
watch: {
selectData(newV, oldV) {
alert("Select data from parent component: " + newV);
this.$refs.form.data();
this.$nextTick(() => {
alert("Show select data after 2sec");
this.$refs.form.data();
}, 2000);
},
},
Although, I'd suggest you shift the watcher inside the child component and access the data prop there.

How to update Vue array of components with a click IN one of the children components

I am displaying an array of child components and in the child component, I need to click some text to make THIS child push to the front of the array.
I've tried it where the child sees the array which doesn't seem to be of any help. And now I've tried to emit with an event to the parent component and I still can't seem to figure out how to push any of the child components to the beginning of the loop with a click IN the child component.
Parent component and method:
<location-item v-for="(location, index) in results"
:key="location.Id"
:index="index"
:location="location"
#changeLocation="changeLocation(location, $event)" />
methods(): {
changeLocation(location, newIndex) {
location.index = newIndex;
},
}
Child components's clickable text and method
<a #click="changeCurrent">
Change Location
</a>
methods: {
changeCurrent() {
this.$emit('changeLocation', this.newIndex);
}
},
So when it's clicked in the child component, it throws THAT child component to the beginning of the v-for loop. The suggestion I'm giving is the closest I feel I've gotten, yet, it does nothing. I haven't seen anyone online really seem to do this so I figured I'd come to ask here.
This is very much a JavaScript question. You are on the right track with the emit from child to parent, need to do some javascript using splice().
As you are already passing the index of the child component, to the component, that is the only thing we need to pass to parent, so...
Child:
props: {
location: String,
index: Number,
},
methods: {
changeCurrent() { // pass the childs index to parent
this.$emit("change-location", this.index);
},
},
Then in parent changeLocation function we use the index to reorder array:
changeLocation(index) {
let item = this.results.splice(index, 1); // remove item from array
this.results.splice(0, 0, item[0]); // put item first in array
},
Here is a DEMO for you. You can also use unshift to add the item.

Emitting object or value in vue.js component child to parent communication

I would like to know which is better way to emit data to parent in vue component. From parent to child I pass prop object but from child to parent with $emit. Should I pass object or value? e.g.: { product } or product.id and also I have to reuse some data in the { product } like product.price on event listener. What should I use? Emit an object or just value then loop and condition in listener function?
Just use a two way sync here:
#product.sync="product"
In the child:
this.$emit('update:product', product)
Whenever you make a change to the property of the product.
This does not work in case of Pass by reference. I mean it works but Vue is screaming don't mutate props directly...
Where movie is an object and genres is an array. Child components props and event
props: {
genres: {
type: Array,
default () {
return []
}
}
}
...
this.$emit('changeGenreList', this.genres)
I had to make the local copy of this array with slice() and then I was emitting this copy.

How to send communication to parent component from child component?

I have split up the components only for the reason the base or the parent component code is growing in size and for code organization. so split the components based on the sections of the parent component.
So the each child component are the each section of the same parent component. But my question how to access the child component objects from parent component? Because most of the examples i see are based on the click event from parent component to view the child component (like dialog) and the value is passed back to the parent html click event and captures the values using emitter and output parameter.
Whereas in my case there is no such click action to trigger the child component. So how to communicate the values from child component to parent component?
In parent component
onDocumentSelect( documentData: IDocument) {
console.log(documentData);
}
<app-document [showDocument] = 'displayDocument' (selectDocument)=onDocumentSelect($event)></app-document>
In Child component:
#Output() selectDocument: EventEmitter<e.IDocument>;
#Input() showDocument: boolean;
I am triggering emit in the function where the data for the DocumentSource is computed.
this.selectDocument.emit(this.DocumentSource.data);
here this.DocumentSource is the dataTable which has value in Child component, that needs to be accessed in parent component.
Apart from this I have similar data for another field of different type needs to be passed to parent component from child component.
I get the below error while page loads
TypeError: Cannot read property 'subscribe' of undefined
In the above code snippet, by changing the type to any solved the issue.
On child component:
#Output() public selectDocument: EventEmitter<any> = new EventEmitter<any>();
On parent component:
onDocumentSelect( documentData: any) {
console.log(documentData);
}

Broadcasting event from parent to child fires all parent's children in Vue.js

I have a parent component and a child component.
In my parent component I have an event on a span:
<span #click="toggle(model.id)" class="open-folder">[{{open ? '-' : '+'}}]</span>
which fires the toggle function
methods: {
toggle: function(id_epic) {
this.open = !this.open;
this.$broadcast('loadTasks', id_epic);
}
},
in this function I call the loadTasks method from the child component with id_epic parameter.
The parent can have n children linked to it, but I only want to fire the first child method not all.
When I click on the span it fires the event for all n children of the parent's tree.
EDIT: The parent and it's children are generated recursively, so the child can also be a parent on it's own.(Like a folder structure)
How can I fix this ?
Thank you.
$broadcast is used to send event to child components. In your parent component you're triggering the event loadTasks from the toggle method. This is good.
You're sending a parameter along in the event, in your case id_epic
Now in your child component, you will need something like this. You need to get the id you're sending in your child component.
events : {
'loadTask' : function(param) {
//check for the id_epic here
}
}
EDIT: The parent and it's children are generated recursively, so the child can also be a parent on it's own.(Like a folder structure)
You should also re-think that part, if you have too many nested components, things can get easily out of hand.
You should be able to stop the propagation of an event by using a second parameter in the child listener. The second parameter is passed the event object to which you can can call stopProgation() and prevent additional children from also receiving the broadcasted event.
Consider adding the following code to your child listener.
events:{
'loadTask': function(param,event){
event.stopPropagtion();
// Handle the param as needed.
}
}
Of course this system of event handling is only for Vue.js 1.0 as in Vue.js 2.0+ event broadcasting and dispatching has been depreciated in favor of a data store such as Vuex. So you may want to consider using a method which will upgrade compatible.
You can try using this.$children[0].$emit() to send the event to the first child instance. though it would probably be better to use a more explicit scheme and have children register handlers with the parent or vice versa.