What are some alternatives in vue.js to passing data to child components other than props - vue.js

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.

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!

Passing data to sub-components in Vue JS. Best practices?

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...

Children to children communication

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.

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