Binding and custom event triggering on multiple level in VUE.JS - vue.js

I am new in VUE.JS but i have finished some beginner courses of vuemastery. Though I know how to bind properties and how to emit custom events, I have a problem: I don't know how to make these things through multiple levels of components.
Let's say we have the following hierarchy:
I need to have control on the Home component's properties from the Elements and Input components from the bottom of the diagram. Right now I am emitting custom events from level to level from down to up, but it doesn't look like an elegant solution.
Is there a better way to do this? And of course when I change one of the properties from Input component it need to have effect on the properties in the Element components as well.
For example the Element components are elements having width and height calculated based on totalWidth property, which can be edited in the Input component. I'm having here a warn as well in the console: [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.

Use this.$root.$emit to emit event on the root component, that would then propagate event on its child components, regardless of depth level

I would recommend using vuex to manage the state of your application. Emitting events all over the place is not the most elegant solution.

Related

Does $destroy function removes the Vue Custom component from cache

I construct deep nested tree of parent and children Vue custom components using my top level component dynamically and then I am updating the data from which all tree is constructed. Which has an effect of rendering the entire tree (its a form with various custom components). I refresh/rebuild the whole form after fetching the data (which is what vue do for reactive data) that itself tell me how to regenerate the view (its like a JSON Schema from which I render the entire view).
This is related to my other issue here.
I am observing a very weird behavior in my Vue Application. When I destroy all my children components and rebuild the data to force rendering the form, it appears that even after I have called $destroy on every child component...Vue is not entirely removing them from cache?
Does vue remove the component from cache if a $destroy is called ?
Because I do not see multiple components of the same type in the Vue component list in the Chrome Vue DevTool extension panel. But I see that the same custom event is handled twice by the same component. Same function that handle the events is getting called twice even though there is only one component visible in Vue DevTools of this type.
This only happens after I render the form. When the page is loaded for the first time every thing works. Then after I reset the form by destroying the child component and resetting the data to re-render the form, magically this child component start handling the event twice.. and in 3rd render it handle the events thrice. But I see only one component in google chrome VueJS DevTool extension panel. So my guess is that vue is still keeping the previously destroyed component in cache. I am trying to figure out how should I destroy those components in the cache.
If anyone has observed something similar and found a solution please let me know.
At the moment I am going to dig little bit more on my component keys (this particular component does not have explicit key set by me).
First and foremost, the vue documentation states:
vm.$destroy
In normal use cases you shouldn’t have to call this method yourself.
Prefer controlling the lifecycle of child components in a data-driven
fashion using v-if and v-for.
So instead of destroying and rebuilding the components manually yourself, you should really letting vue handle that via v-if and v-for. If the components aren't updating to your changes, you might be dealing with a reactivity issue.
As you mentioned that this is a deeply nested structure, the reactivity is key to keeping the components up to data with the data.
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'b', 2)
Inside of a component:
this.$set(this.someObject, 'b', 2)
Also, keep in mind that Vue should be doing the heavy lifting in regards to component management, while you should define the parameters by which a component is rendered.

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.

Vue - use Events, Event Bus, or Vuex for showing components

I'm currently building a larger scale vue application. For global state management I use vuex of course. However, I came across the question of how to show or hide really simple components. For example I have a navbar which can open up a bug form modal component. I also have a sidebar component which can be either visible or non-visible. From within these components I want to be able to close them in the parent. Own mutations in vuex seem a little bit overkill for simply toggling the visibility of a child component, also I don't want the store to get bloated by these things.
My question now is, how I should handle showing these components. I can think of 3 different solutions:
1) Use solely Vuex for everything, with own actions, getters and methods for showing or hiding simple components.
2) Use an Event Bus to show these components along with vuex for storing user data like email for example
3) Just emitting events in the child component and listening to it in parent component

How to communicate between components within a certain component?

Suppose I have a tree of component like this:
<widget>
<widget-header>
<panel-toggle></panel-toggle>
</widget-header>
<widget-body>
<panel></panel>
</widget-body>
</widget>
Now supposed I want the panel-toggle component to be able to toggle the visibility of the panel component. I could have it affect a prop passed down from widget through to each component, but that didn't seem like the best solution. I tried sending an event with this.$emit(eventName) but the event is only picked up by the immediate parent of the element emitting the event. In this case, that would be panel-toggle emitting the event and only widget-header being able to pick it up. I tried sending the event across the root element with this.$root.$emit(eventName) and picking it up with this.$root.$on(eventName), but then it is picked up by all widget components and that is no good. What I ended up doing is sending the event with this.$parent.$parent.$emit(eventName) and then picking it up from panel with this.$parent.$parent.$on(eventName). While that worked, it doesn't seem like the right way to go about this.
What would be the correct way to achieve this communication between components within the component widget only with Vue? Is the answer somehow related to the ref feature?
Since you're concerned (and with good reason) about the globalness of a global event bus, the solution is a localized event bus. Create a data item in the parent:
panelBus: new Vue()
and pass it to each of the children as a prop. Now they have a private communcation channel for just the two of them.

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