Vue: how to fire an event on a sibling component - vue.js

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.

Related

Can a Parent and Child listen to same event and merge data together - Vue?

If I have a button in the parent class, and a child class stores certain data. The parent class also stores different data.
When we click on the parent's button, is it possible for the child class to listen to this event and then pass this data into the parent, then the parent also grabs its own data and merge this data together?
How can they both listen to the same button event, and then retrieve data and pass data to one another? The data needs to be passed through another function to format/change the values too, not just displaying this data.
Is this possible and how, since this button seems to affect two components and data is passed along?
Thanks!
Children cannot catch parents' events. Children can expose methods that parents can call, or you can create a reactive property that represents whatever state changes when the parent button is clicked.
Events are messy and low level and you shouldn't mourn not being able to use them. Using state and reactivity is much cleaner. Vue components should really only store "private" state. Props are available for passing state down to immediate children. It's much better to extract state into a) a plain old javascript object, passed around via data or provide/inject, or b) vuex, then watch reactivity work its magic.
When you define your data, you can reference anything anywhere. So if you create an object in global scope, let's call it $myState...
Vue.prototype.$myState = { myProp: "hello", myName: "Simon"}
then you can reference it in your data in any component...
data : function(){return { myState : this.$myState }
Some people will object that doing this creates spaghetti - anything anywhere can modify your global state object, but for many many applications, the simplicity gained by "normalizing" state and storing it centrally will outweigh the costs.

Should I create the deep copy of Vue component parameter?

From Vue documentation:
All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around.
However, if to pass the nested object or array from parent's data as child component's parameter and child will change it, the data of parent will change, too.
Ideally, good framework must take care about deep copying when required, but Vue does not.
The one of solution is creating the copy based on parameter's value. Should I do it?
I dont think creating copy is a good solution as far as i know.
The best practice for your task is to use .sync modifier!

Modifying child data from parent vue

I have a component that represents an option in a form, with data representing the currently selected option. There is a parent component which represents the full form, with a submit button and a reset button. I keep track of what options are currently selected in the form by emitting events from the child to the parent (this is important because the form updates dynamically)
.
I'm trying to design the reset button, which clears all fields in the form (sets the currently selected option to an empty string). I would need to modify the data of the child component. Should I do this using a Vue instance as a bus? That seems overkill. Is there a better way to design these components?
I think you want to use sync on the properties your passing into the child component. I use it to load my child component like:
<textbox :content.sync="new_comment" placeholder="Add a comment..."></textbox>
If you already emitting from your child component then changes to new_comment will automatically be passed through.
You can find a lot of ways to do this here.
For me, after a lot of playing around with props, i found that the best and safest way is to use this.$refs.
Even if you have more than one child component with the same ref name, you can go through each child with a forEach.
You can create a custom event to listen to the reset button on each form field. Check out the documentation for this here
Just put a method in the child, perhaps Clear, and call it from the parent. You use $refs in the parent to get to the children.

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.

Vue common component communicate with different parent

I have a requestion, how about vue.js's common component's best practice when communicating with different parent component.
for example, in my scenario, a basic modal component, trigger a 'close' method, but it has two different parent component
I find two solutions:
parent need pass an additional prop, and then baisc component just
trigger event which event's name is the prop value, so the listener
parent component attched on could be called
in basic modal just use this.$parent to visit parent component methods, or this.$parent.trigger('xxx'), and then parent knows what to do
But, both above I think not very good, the first may need pass an additional prop, this let others who write a third, a forth parent component use the basic component not very handy. And the second may be felt more hard coded.
So, is there is better solution in this case?
Use this.$dispatch('eventName', data) (for Vue 2.x, use this.$emit('eventName', data)), to trigger an event to any parent, grandparent and further up the chain (you can use this.$broadcast('eventName', data) to trigger events down the chain in Vue < 2.x).
If the parent has an event called 'eventName' then it will fire this event.
If you have multiple parents, you can give them each a different event and from the child fire this specific event via dispatch. You can also give each parent the same event and pass a data prop that specifies what the parent should do. Third option is to refer to the specific parent:
var parent = new Vue({ el: '#parent' })
// access child component instance
parent.$refs.eventName()
Each option has pros and cons. The best solution depends on the context. But I think that the best solution in general is option 1. Then you don't need an additional data parameter. Option 3 is not loosely coupled.
For more info: https://vuejs.org/guide/components.html