Vue.js two way binding using a custom type - vue.js

I thought that two way data flow was not just discouraged but impossible between parent and child components in Vue.js. However I've discovered it's actually possible using a custom class as a prop.
Custom class:
class MyClass {
constructor(val) {
this.val = val;
}
}
Parent template:
<div>
<child :obj="obj"></child>
</div>
Child props:
props: {
obj: MyClass
}
Child template:
<div>
<button #click='obj.val="changed"'>Change val</button>
</div>
Here is a working example: https://codepen.io/francoisgaudin/pen/XWdBOxN
So now I'm wondering why this is possible - is it deliberately allowed or is it a loophole that should not be exploited??

I found the answer in Vue.js documentation on the "one way data flow" (https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow). They specify the following caveat:
Note that objects and arrays in JavaScript are passed by reference, so
if the prop is an array or object, mutating the object or array itself
inside the child component will affect parent state.
So one way data flow is not strictly enforced and there is no error or warning in this case. This is a loophole - (which is best avoided).

You're able to modify the object because object references are values in Javascript - which makes objects act like they are passed by reference.
In other words:
Javascript always passes by value, but for arrays and objects the value is a reference to the object.
https://medium.com/nodesimplified/javascript-pass-by-value-and-pass-by-reference-in-javascript-fcf10305aa9c
It's allowed because the alternative is copying the whole object (which could include nested objects and is not really practical).
Also, this is an opinion, but you should not do this.

I do not see a downside of using it, more than loosing the track where the object is modified in a very nested child components. I will play around a bit more with your codepen, I also wanna know why it is possible, and Avoid mutating a prop directly error is not raised.

you can use Vue's $emit feature to communicate between child/parent, more examples here :
https://v2.vuejs.org/v2/guide/components-custom-events.html

Related

Does each child add reactive getters and setters to props?

Imagine you have a huge list of things to do:
[
{
id: 1,
name: Pet a cat,
priority: 'extreme'
},
...
]
The app has three components grandparent -> parent -> child
The todo list is declared in grandparent's data function. This is where the list becomes reactive first. The list is then passed from grandparent to parent and from parent to child as a prop. From my limited understanding each component in the chain adds their own getters and setters. My concern is that this way of passing props is not optimal for performance. I mean, should I restructure my code in a way that minimizes such props passing or not?
First of all, Props in Vue are readonly. You'd get a runtime error if you ever try to update one. So actually there's no setters for parent and child components, only for grand-parent.
If your child components wants to update it, you'll have to send events until some component can actually update the data.
Second, you won't have any performance issue with it, it's the way Vue works and it's good at it. Actually it's the proper and most straightforward way to achieve what you want. Obviously, if the parent / child list extends even more, it's gonna be a pain for you to use only props + events, but no performance issue I think.
The other solution to avoid data to be passed through each component descendent is to use a "Vuex Store". This is not super easy to set up and understand for beginners though. You may give it a try if your app is becoming more complexe.
I'd suggest you to stick with your current solution as it has nothing wrong.
Happy coding :)

Angular update parent data from child component

Yes, this old chestnut.
I'm making an editor for a large object tree, and it seemed sensible to make components for each of the property types at a vertex in the tree in order to remove clutter from the main component. However, this has created the problem that the main JSON object isn't being updated beause the editor is implemented in child components (#Input).
Using #Output and EventEmitter seems infeasible because:
how on earth do you get a component to call the emitter very time a change is made to the data it's editing, and
it seems really stupid to have to write an update function to handle the event because the UI should be just automatically bound to the JSON object it's editing -- that's the point of this observable malarkey.
The only solution I can think of is to not use components and just wirte a massive monolithic editor.
Is there a better way?
The way you describe is pretty standard. An alternative though would be to use a template # variable to access your child properties.
<hello #childcomponent name="{{ name }}"></hello>
<div>
{{ childcomponent.name }} - Parent component
</div>
You can use this to access your child component through the typescript as well.
#ViewChild('childcomponent') childcomponent;
Stackblitz

Using Intertiajs with Element UI

I am using Inertiajs
with Laravel and also trying to use Element UI components but as i use Menu component i am having following error in console, I just used example as given in Element Ui Components as i was testing.
I see 2 different errors in there, both of them are with props.
I assume your component is taking the route as a prop, and you are also using the route as a method, which you might have put inside methods: {} which is not allowed. Make sure you rename your method route to something else.
Note: As a matter of fact you can't have any data coinciding with each other. your props, data, computed props and methods all should have unique names.
You are trying to use v-model on the props directly which won't work in Vue. if the prop is a primitive (Number, String, Boolean etc). but you can pass Object or an Array which can hold a reference to the data. This is because reactivity in Vue can't keep track of props when passed as primitives.
More on prop mutations here: https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow

Use of _self attribute from Vue vm is reliable?

I need pass the main component to a deeper child. Currently I am doing on child this.$parent.$parent, but is not reliable for me, because this same child could be in a third+ deepth level.
My idea is transfer the main component reference between childs, like:
<v-some-child :main-component="_self"></v-some-child>
So I can use the main component instance where I think need by passing the _self or this.mainComponent data to a more inner child.
My doubt is:
_self attribute is reliable to points to component Vue instance? Seems that _ prefix is to internal attributes, and $ for public (reliable). If it is true, what I can do use?
There are some better way to do what I wants?
Thanks!
Edit 1: as an immediate more reliable workaround, I am using data() { self: this }, then I am using :main-component="self".
I'm unclear where _self is getting set in your case. Typically _self = this; is an approach used by developers to pass the the current Vue instance via closure to a callback function. I wouldn't trust a _self unless I knew exactly where it was getting set.
Your edit1 should absolutely work. As long as data(){ self: this } is happening on the main Vue instance and in the components you use self when setting a property via a : like :main-component="self" then that should work fine given that the main Vue component data properties are merged into all components used by that vue instance.
It's kinda a cleaver approach frankly. As #raina77ow mentioned it may make sense to look at VueX or an EventBus but the approach you propose in Edit1 should be a reliable way to pass around the main Vue instance.
As an aside, I wonder if root or main might be a better name then self. Given that it's the "self" of the main or root level Vue instance not the the "self" of the child passing the value.

Vue.js bind v-model input with prop initial value

I am trying to pass down a initial value to a select input bound by v-model. I cannot figure out why this doesn't work:
props: ['team'],
data() {
form: {
data: {
country: this.team.country
}
}
}
The form.data.country is undefined. Although, the props data is actually passed down. I can access it with Vue Devtools like $vm0.team.country and I can print other data from the props. However, it is not registred in the data().
Also, when trying to debug using mounted(), the property, team, is not defined.
mounted() {
console.log(this.team);
}
But, as I stated earlier, it is defined when the template is rendered, and can be used like this.
<input class="input" type="text" name="name" :value="team.name" disabled>
Why is the properties I am passing not beinged recognized in data()?
The asynchronous loading is not the problem. Even if team is hardcoded above, it's still undefined when data is created in the component. (The component needs to be created before the root Vue that passes the prop.)
There is, perhaps someone will correct me, never a good reason to reference props in your data. Something is either data (the current component knows where to find what it wants) or it's a prop (the component will let it's context supply the info).
Then, as you've discovered, your data is created once. Vue watches everything for changes, but you're vulnerable to changes of the root value. If the thing referenced as a value when data is created, changes, the reactive pipe is broken. The answer to this is the store pattern. You watch a variable in global scope, that never gets replaced, even though it's contents may change.
code
The props data was asynced loaded by the parent. This was suggested in a comment by #Bert