Emitting data from a child component? - vue.js

I have a child component, when data changes on this component I emit the data, I do this like so:
<input type="checkbox" value="john" v-model="users" #click="updateUsers">
When the user clicks on the checkbox, it runs the updateUsers method, which emits the this.users value.
updateUsers() {
this.$emit('users', this.users)
},
The issue is that the #click method runs before the v-model users updates. So nothing is emitted.
Am I approaching this in the wrong way? Basically I want it so that when the data changes on the child component, the parent is made aware of it and is passed the data.

I think you want to be using #change instead so you can catch the instance of the value being updated then send the emit.

When you want an updated variable to be emitted, watch the variable. Only look at the DOM when there's no viewmodel state to tell you what you need to know.
By using a change event to emit a data item, you're mingling concepts and depending on order of execution. The change event does not guarantee that the data item is updated by the time the handler fires. If you are going to emit from an event, you should emit event.target.value, not the data item which is updated separately.
If you want to emit the data item, you should be watching it for changes.
Since you're emitting changes, your component likely shouldn't own the data item, it should receive it as a prop.

Related

Why does Vue update variable in parent - no event used

According to Vue.js docs (Components: One-Way data Flow) I assumed that I'm not able to update parent's variable from child component.
How is it possible, that when I bind a variable to child component, it is updated without emitting any event?
Here is Vue SFC playground example.
How can I use a copy of variable in child component without propagating changes to parent?
Edit: I'm curious why the property userAccount in AccountPage.vue gets updated.
There's no v-model, no events submitted, yet it still gets updated.
Edit:
In AccountForm.Vue an reactive and mutable ref object is created when writing
const userAccount = ref ({..})
Changing the reactive object will affect the parent. If you remove the ref() you'll see that the parent isn't updated.
https://v3.vuejs.org/api/refs-api.html#ref
Old answer:
When you use v-model you create a two-way data binding. V-model is a shorthand for v-bind and input, that is you bind data to child via v-bind and receive data via input event that is emitted from child.
So,
<input v-model="searchText">
is the same as writing
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components
The example demonstrates the usage of v-model and as stated in the docs
Custom events can also be used to create custom inputs that work with
v-model. Remember that:
<input v-model="searchText">
does the same thing as:
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value">
meaning it does fire an event

Use $emit without a click event

I'm new to Vue and there are some things I didn't understand yet.
In the first child there's a click event to $emit information to the next child. But in the other one, the second child, is there anyway to pass the information to the next child using $emit without a click event or any other event (like scroll, hover etc)? I just want to pass it ahead. Can I use mounted()?
Yes! What you're looking for is $emit(), in combination with the v-on directive, both of which it sounds like you're already using.
In the child component that you mention, the lowest one on the chain, you're already capturing a click event from some component, and then presumably using $emit('my-event', [...some data...]) to emit a new event out from the child component itself.
All you need to do now is to add event listeners and handlers in the components up the chain, using Vue's custom events mechanic, so that they can receive and emit their own events, and the data can make it up to the parent. Custom events allow your components to emit and listen for events of any name, which may carry any data they'd like (this means they're not limited to being click/ hover/ blur/ etc.).
Here's the step-by-step of that approach:
(Structure: Parent <--> Child #1 <--> Child #2 <--> Child #3)
Child #3 captures a click event, and emits an event in its handler
Child #2 is listening for this event on #3, captures it, and $emits its own event in its handler
Child #1 is listening for this event on #2, captures it, and emits own event in its handler
Parent captures event, and executes handler
In code, you'll have a listener and handler in each of your components, which can look something like this:
(I've used inline handlers here for brevity, but you can use defined methods as handlers too, just call this.$emit(...) from the method.)
Child #3:
<component ... #click="$emit('my-event-a', $event)" />
Child #2:
<ChildComponent3 ... #my-event-a="$emit('my-event-b', $event)" />
Child #1:
<ChildComponent2 ... #my-event-b="$emit('my-event-c', $event)" />
Parent:
<ChildComponent1 ... #my-event-c="myHandler" />
$event here is just special Vue syntax for referencing the event data in an inline handler, which in this case allows your new event to contain the same data.
The events in the various child components can be named whatever you'd like, just make sure that the respective listeners are listening for the correct event name (eg. #event-x="..." for $emit('event-x') and #pizza-eaten="..." for $emit('pizza-eaten')).
I also wanted to mention that passing the event object (or any data at all) with an emitted event is completely optional. To do so, just invoke $emit with the event name and no other arguments: $emit('my-event'). This is useful when you simply care that an event has occurred, and the handler requires no further info.
There are many ways to approach this problem, but this is the most straightforward to understand, and works great as long as you don't need tons of direct interaction between a component and its deeply nested children.
If you do find your component structure getting more complicated, then you may want to look into a fully realized state management solution, like Vuex, which allows your components to interact with a state manager rather than having to deal with tons of events through a complicated component structure.

Vue: how to fire an event on a sibling component

In Vuejs, I have a parent component (P), and two child components (C1 and C2). The parent has objects which it shares with both child components.
Component C1 displays the objects (in a table) and component C2 lets the user modify the properties of the shared objects. Because the objects are shared as objects, I am updating their properties directly in C2, and the changes are displayed in C1.
The problem is that C1 is responsible for saving changes (by making an ajax call to the server). If the user modifies a property directly in C1, it triggers the change event, and an event handler makes the call. However if the property is updated in C2, the change event is not fired in C1, so the change is not persisted.
So, the question is, how do I fire a change event in C1, based on an event in C2? I have an event bus in place (in the root element). I could (perhaps) use an event listener to grab the element (e.g. with jQuery), and fire the event, but obviously this is not the most elegant way to do it. What's the Vue way?
I hope I understood you correctly: You're sharing an object between two children and you're manipulating that object in either child component directly?
That pattern itself is the problem. You don't want to manipulate state inside a child component. The clean way that'll keep the reactiveness is to bind the object via a prop from the parent to both child components. The children should never manipulate the state of the prop object (vue should actually throw a warning if you do that), they should only emit an event that they want to change the object by using:
this.$emit('input', newObject)
Now simply listen to that event in the parent and manipulate the object there, or, for simplicity you can also use the v-model pattern as described here.
<c1 #input="myUpdateFunction" />
or with the v-model pattern
<c1 v-model="mySharedObject" />
That's it. If your other component has the same object bound to it as a prop as well, it'll update its template automatically.
If you want an even more elegant way to share state for an entire application I suggest using Vuex.
Your question it is not too clear.However you can use eventBus and it will work fine.
My recommendation though,is to use vuex.
You can manipulate the store properties and whenever the properties changing,the changes will be reactive and applied to all components.
So if you have two components c1 and c2,and in store you have a property name: 'John Doe'.
If in c1 component you change the name like: this.$store.state.name = 'Jane Doe' this change has also take effect to component c2.
Of course it is not so simple.In vuex store you should have state,getters,mutation,actions and on your components to use computed properties to use fetch the store properties though getters.

Vuejs - Set "global" prop to edit all properties without using v-model

Assuming that I have this component below:
<c-attachs v-for="item in attachs" v-bind:path="item.path"></c-attachs>
And try to edit some property directly from some method, such as:
methods: {
changeProp: function ()
{
this.path = 'myNewString';
}
}
Vuejs warns on the console with the message:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders
But... if I set "v-bind:allprops="item" and edit property directly through "allprops" object (such as code below), it works fine without error. My doubt is... Is this the correct way to edit property on events without using v-model?
this.allprops.path = 'myNewString';
There is no correct way to edit props, because you are not supposed to edit them.
Every component should have complete control over its own data. That keeps behavior easy to reason about. Items that are passed to children as props should be considered read-only, so that the owner of those items retains control. That is why Vue has events.
When something happens in a component that should affect data that the component doesn't own, the component should issue an event so that the owner of the data item can handle it. If the owner of the data item changes its value, that change will flow down through the props.

Is it idiomatic to update a property of a prop, without an event?

I pass the list of inputs as a prop,
v-for input in inputProps
v-model input.value
Should I be using events, instead of v-model?
Passing an event to manually update a property of a value in a list would involve
[index]'path to property', would it not?
It is idiomatic for props to flow down and events to flow up.
when the parent property updates, it will flow down to the child, but
not the other way around. This prevents child components from
accidentally mutating the parent’s state, which can make your app’s
data flow harder to reason about.
In the case of the v-for, yes, you would need to indicate the index of the item so the parent could take appropriate action on it. Depending on what you're doing, exactly, you might also catch the native input event in the parent and process it there, so that the child component is not involved in the transaction.