I would like to know the best practice for implementing shared states between components in Vuejs.
Imagine situation A: you have a web app that shows a modal. The modal has the boolean state show. This state should change if the modal OK-button is clicked, but also if any part of the background is clicked, and perhaps even on some server pushed state change. Thus the modal should be able to change the state as should the parent app.
Situation B: you have a web app that shows input fields inside different components that share a common data value. If the user changes value through the field in one component, it should also update in the other. Again both should even update on a server push event.
Questions:
I am right that the correct way to go about this would be to use vuex and make the shared state a store field that is observed by and changed through emitted actions by all components / parents that need to modify that value?
Does that not introduce this kind of dangerous (since hard to handle) magic reactivity that we know from Meteor?
How to best document the flow, what depends on what?
A: For a modal component, I'd say that show should be a prop. So the parent component can control the modal whatever it wants. In this case there is no shared state at all.
The modal itself doesn't need to know anything about the server. If the prop show is true, just display the modal and vice versa.
I think the mask layer is a part of the modal, so when the mask is clicked, the modal emits an event. The parent component receives the event and can decide to hide the modal or not to.
Vue has an official modal example here (thanks #craig_h for mentioning): https://v2.vuejs.org/v2/examples/modal.html
B: Just bind the vuex state to the inputs. Nothing wrong.
Note that not all the components need to access the vuex store directly. For some pure UI components, just use props. So the parent components have the right to control them and increase flexibility.
I recommend you to read these docs:
https://facebook.github.io/react/docs/lifting-state-up.html
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.j7ry4a3as
http://redux.js.org/docs/basics/UsageWithReact.html
Yes these are React / Redux docs. Since Vue is relatively young, react community has more documentation / articles. But both Vue and React are component-based libraries. The idea of how you design a component is basically the same.
You can also take a look at this vuex example: https://github.com/vuejs/vuex/tree/dev/examples/chat
This is a very simple example but it does use all the things I mentioned above. Emitting an event, some pure UI components...
Related
I need recommendation about "the right way" of vuejs layout structure.
I have header, which is the same for whole app but it (header) has one block that changes, depended on which child app I have. Currently it's a parent App.
What is the best solution for that?
multiple parent App
use header as a component with a slot
any better way ?
Accordingly vuejs recommendations, a child component should never change it's parent, instead you can use events or vuex, and then your header will behave accordingly.
https://v2.vuejs.org/v2/style-guide/#Implicit-parent-child-communication-use-with-caution
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.
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
I was Vue.js now for a project and created and used a lot of components.
Now I started to have the problem of having too many eventemitters and props that I need to keep track of.
I guess to illustrate the problem the best I will use an example:
Lets say you have a main.vue and 2 or 3 Components.
One contains a button that should manipulate the other 2 components or switch them out.
Now I need to emit an event to the main.vue and then main.vue has to change a binded variable and pass props down to the other 2 components.
Alright: Now lets put the button in a component of of a component. You need to make sure that every link between a parent and a child is correct.
Now create a bit project and put a button in another components and you have to change everything.
So is there a good way to avoid this?
Something like a broadcast function so that every component is receiving the event?
Or a Manager that is handling the communication of all components?
use a flux pattern (vuex)
At first you may think that this does not really answer the question, since it deals with storage of data, and not handling of events. The flux pattern changes the architecture of your application by creating a single store (think database) that all components can read and write from. Coupled with the reactive nature of the reactive frameworks such as vue (or react), the components will react to a change in data. So instead of tightly coupling component A to D through B and C, you'd have component A listen to mutations in object X, and component D makes changes to object X. When the change happens, component A gets updated without having to listen to any of the children's $emit functions firing. At first it may seem daunting, but the investment is worthwhile.
I've got an existing SPA that was developed using nested RactiveJS components. Which is great, and offers a ton of flexibility throughout the entire app. Currently I attempting to add in client side routing support using page. My navigation switches out high-level components using simple {{#visible}}{{/visible}} template markup on each component. This is a little troublesome in its current state as it always kicks off a re-render whenever the high-level component becomes visible again.
Is there a way to render a component, for example, called widget, without using the
<widget></widget>
method? I've already "registered" the component with the parent, but obviously when constructing it by means of
new App.components.widget
I am able to control how/when it's rendered/inserted/detached, but lose the recognition in the application's component hierarchy.
There is insert exactly for that. You don't even need to "register" it to the component you plan to put it to. You can use the different find* methods or nodes to easily retrieve a reference of your planned container element.
var instance = new YourDetachedWidget({ ... });
instance.insert('#your-container'); // This could be a node, selector or jQuery object