My component is pretty big so I will give you simplified example, maybe someone will know how to solve my problem.
I have invoice component with children components like 'subtotal', 'vat_subtotal', 'total' (it's example). I'm using v-ref to get direct access to every child from invoice component. Also, subtotal is calculated from invoice properties, then vat_subtotal is calculated from subtotal children properties. And 'total' is calculated from vat_subtotal children.
Example:
invoice.$refs.subtotal.total = {some calculations}
vat_subtotal.total = #$parent.$refs.subtotal.total * 1.21
total.total = #$parent.$refs.vat_subtotal.total
The problem is that i'm getting warnings when page loads, be cause 'total' children is trying to access 'vat_total' children properties, but #$parent.$refs.vat_total is still 'undefined' (I don't know why. When later im changing something in form, it reacts normaly and recalculate everything right). It seems, that one children is trying to compute properties while other children isn't loaded yet.
Don't reach into a child for data. If the parent needs access (in your case, to give access to another child), the data item should be created in the parent and passed as a prop to the child. That is how data can be shared among multiple children. Child components should not depend on other child components being available through the parent (or on anything in the parent, really, if it isn't passed in as a prop).
If the child component is responsible for changing the prop value, you can pass it using .sync.
The way you are trying to solve things is technically possible but highly discouraged. As stated in the docs:
Although it’s possible to access any instance in the parent chain, you should avoid directly relying on parent data in a child component and prefer passing data down explicitly using props. In addition, it is a very bad idea to mutate parent state from a child component, because:
It makes the parent and child tightly coupled;
It makes the parent state much harder to reason about when looking at it alone, because its state may be modified by any child! Ideally, only a component itself should be allowed to modify its own state.
In general, you should aim for creating a model representing the state of your application. Instead of accessing parents / children directly, pass any relevant data down to children using props. Children can notify their parent about changes using events, or you can use .sync binding to synchronize models automatically.
I think that you would benefit from reading about more structured approach to Vue application architecture. I'd start with Flux-inspired Application Architecture for Vue.js.
Related
I have a relatively complex API request object I need to make, with a large number of UI components responsible for updating different properties of the object.
I'm passing the basic request model as a prop from a parent component to its children, which pass it on to theirs (down several "generations").
At the parent level, I have a computed property that returns a field of this data model, and a watch on that computed property.
When a child component updates the property on the model, it successfully updates everywhere that has a reference to it, but the computed property on the parent fails to recalculate, and resultantly the watch never activates.
I'm guessing I've missed the point somewhere along here, but I can't think about how else to update without resorting to long event chains through the UI.. How should I be approaching this instead?
To anyone with a similar question - from my research it seems that modifying reference values on props is not the intended approach for VueJS. Which is a shame, because initially it seemed like quite a neat pattern.
I've implemented vuex now, which is working well, and avoids long lines of events going back to the original owner of the prop data.
IF you wanted to press it, then modifying references on the object itself will force updates down the chain. So (e.g.) if you wanted to update an array property of the prop data, then instead of "pushing" to it, you would replace the whole array object (causing other components with computed properties on that array property to recalculate). But again, not recommended.
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.
I know the correct pattern to pass data from/to a parent/child component in vue.js is to pass props from the parent to the child and emit events from the child to the parent. But is there anything wrong with this:
this.$refs['child-component'].setValue(val)
I'm told that one should use refs to access child components sparingly, and only when there is no other way.
What if I want to update the value of a field in an array in the child component that isn't bound to a prop, something like this:
this.$refs['child-component'].childArray[index] = val;
...where childArray is not a prop of child-component? Do I need to MAKE childArray a prop of child-component? But then it becomes the parent's responsibility to maintain childArray. What if it isn't the business of the parent component to maintain childArray?
Thanks.
Actually, it is not a business of the parent to mess with the child's internal data at all. You should not directly mutate internal data of a child - instead, consider calling a method or emitting an event (which should be handled by the child).
Currently you are creating a tight coupling between the parent and the child - now the parent is overloaded with knowledge about the child internals. Tomorrow, when you decide to change the implementation of the child (for whatever reason) - most probably you will also have to inspect all parents where this child is being used just to check that your changes did not break the assumptions of those parents.
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!
I'm confused about best practices for passing data from parent to child components and modularity strategies.
As I know there are 2 ways:
Fetching data in parent component and send Array/object to the child via props
Send parent_id to the child via props and fetching data within the child component
Let's assume a use case working with a product edit view, having:
A parent component product
A child form component to edit basic product information
A child related_products component where other products can be linked/unlinked.
As per my experience, the first way works smoothly since it's all done in 1 request to the API: fetching a product object in parent component and passing through props the product itself to the form component and the nested objects to the related_products component. In addition, it can be done in beforeRouteEnter guard so the parent and all its children are shown with all the information at once. The cons I see here is that we have to send the correct object structure to the child component to avoid any error, having a strong dependency between components.
On the other hand, by sending the parent_id to every child component through props we release the parent from any logic. In this case, every child component acts as a "black box" and fetch/handles the information. The cons I see here is that we would have to perform 2 API requests for getting the product and the related products. In addition, the beforeRouteEnter is not viable at all, so we get an empty form/table until the data is retrieved.
This is not about how to code it, it's just about what's the best implementation as per your experience.
As far as my experience is concerned, 1-st way is better. Parent component acts as "smart" and you have access to it's life cycle hooks in order to trigger your api-requests, reset timers, e.t.c... I would also suggest to use vuex, as it allows you to make clean interface of communication between your parent component and "outer world", utilizing actions and getters.
With that being said, your "dumb" child component communicates with it's parent through props and emits interface. And because it is "dumb" - it's easier to test it or utilize something like "storybook".
we have to send the correct object structure to the child component to avoid any error
I guess, at the end of the day, you'll need correct object structure anyway, right? It could not be just random...