How v-model make a property of object reactive? - javascript

Link to my project:
https://codesandbox.io/s/v-model-3j96f
As of my link above, the is a file named HelloWorld.vue inside the "components" folder:
inputvalue.bbbb is a reactive data which is defined in data option, but
It's weird that inputvalue.cccc will become reactive after input with the v-model, but inputvalue.cccc will not reactive with #input.
In this question (Vue.js bind object properties), the first situation should not be possible.

Using v-model will automatically use $set to set the values on nested properties. This ensures this it works with array indices, as well as working for object properties that don't exist, as per your example.
If you're unfamiliar with $set it is documented here:
https://v2.vuejs.org/v2/api/#vm-set
The code for this part of v-model in Vue is here:
https://github.com/vuejs/vue/blob/399b53661b167e678e1c740ce788ff6699096734/src/compiler/directives/model.js#L44
In your example there are two inputs that use cccc. As you noticed, if you edit the input that uses v-model then everything works fine. However, if you use the :value/#input input first then it doesn't work, even if you subsequently use the v-model input. The behaviour is, somewhat oddly, determined by which of those two inputs you edit first.
The reason for that can be seen in the code for $set:
https://github.com/vuejs/vue/blob/399b53661b167e678e1c740ce788ff6699096734/src/core/observer/index.js#L212
The problem is that $set will only add a reactive property if the property doesn't already exist. So if you use the :value/#input input first it will create a non-reactive cccc property and once that is created it can't be made reactive, even if you use $set. It would have to be removed using delete before it could be re-added reactively.

Related

Vue - access input value inside the input's attribute

Is is possible to access inputs value inside it's definition/attribute?
For example, if you want an input to be green if not empty but you don't want to use Vue.data.
Like this:
<v-text-field background-color="'green' ? <THISVAL> : 'red'"></v-text-field>
Or do I need v-model and variable defined in the Vue.data?
Yes, you do need v-model and variable defined in the Vue.data.
Why? Remember that vue uses virtual DOM. When this template is being processed for the first time, no actual element is present in DOM, to be access via this.
It needs to know what to render into DOM beforehand.
There is component-template-refs to gain references to actual HTML elements, but these references are assigned after the component is mount, and not needed for use cases like this

Optionally pass data to child element via directive

I have a popup dialog box that I wish to display one of two slightly different views, dependant on whether I have data present or not.
Is there a way in which I can optionally pass in said data within the directives?
ie. I would like to use something like
<my-comp *ngIf="ifPopup" [data]="myData" [isNew]="isNew"></my-comp>
where the [data] may not always be present, ie. It may either be undefined or have actual data present.
I just want to avoid having to basically duplicate my component.
Update after confusion around my question
Basically I am adding a new record to my DB (imagine adding a new customer etc). My component will either be used to edit a record or create a new record. If I am editing a record, myData will be filled with this record. If I am creating a new record, myData will be undefined..
why don't you just use
<my-comp *ngIf="ifPopup && myData" [data]="myData"></my-comp>
in this way, If the myData is not present, your popup wont display
Or you can also use <ng-container> and wrap your component inside and and use ngIf for your data!

The template cannot be rendered by Object in vue.js

In this demo, (https://jsfiddle.net/ccforward/fa35a2cc/) I cannot render the template and the data resultWrong equals {}
In this demo, (https://jsfiddle.net/ccforward/zoo6xzck/), if I use a temporary variable to save the async data ,then I can get the result and render the template
If I add another function named as getRightData() in the methods, then the getWrongData() can work and the template can be rendered.
link: https://jsfiddle.net/ccforward/7f42owpc/4/
If I delete the getRightData() method, then the getWrongData() cannot work.
link: https://jsfiddle.net/ccforward/7f42owpc/3/
Vue cannot detect properties that are added dynamically to an object unless you add them using set.
Here is your first fiddle updated to properly add properties to an empty object using this.$set.
For your demos, the first does not work because you add the properties using an index and Vue doesn't know that it needs to update the DOM.
The second demo works because the base value, resultRight, is set to a completely different value. resultRight is a reactive value and when it changes to a different value, Vue is aware that it needs to update the DOM.
The third demo appears to work, but it only works because resultRight changes, and because it is reactive, Vue knows to update the DOM. resultWrong is rendered at the same time but only because Vue rendered it based on the change in resultRight.
The fourth demo fails for the same reason the first demo failed. resultWrong gets new properties, but Vue doesn't know about those properties. And because you are not changing the object reference (as when you change resultRight to tmp), Vue doesn't have any idea it needs to update the DOM.

Vue.js get all v-model bindings

With Vue.js you can you bind form input to a property:
https://v2.vuejs.org/v2/guide/forms.html
I am working on a project where I 'dynamically' generate a form though. Here the v-model bindings are just attributes that are being set based on the name that an input has.
The problem though is that I am left wondering how to properly get these v-model bindings. So far all I have see is people manually setting a property on their Vue instance for the binding:
new Vue({
el: '...',
data: {
checkedNames: []
}
})
Is it possible to somehow grab all of the v-model bindings though without setting up the data properties manually?
When creating a dynamic form with a variation an dynamic number of fields this would help out a lot.
I guess one option is simply retrieving the name atributes from the input with javascript but I am hoping there might be something in place already since v-model is probably calling a setter that might already be known in the Vue instance.
Reworked the example from the answer:
https://jsfiddle.net/yMv7y/2477/
According to the official documentation:
it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method.
This means that your starting data can be something like:
{
customForm: [],
values: {}
}
Let's assume customForm is an array of field objects that describe a bunch of fields in your custom form. It's empty at the beginning because you say your form will be created dynamically.
Once you generate that dynamic data, all you need to do is assign it to customForm and loop through it to add reactive properties with the aforementioned Vue.set method.
Here's a JSFiddle with an example. I'm using this.$set, but that's just an alias for Vue.set.

Items added to list are non-reactive in version 2 of Vue.js

In Vue 1.0 I used to add items to an array used in a v-for by simply calling items.push({}) as you can see here:
http://jsbin.com/figiluteni/1/edit?html,js,output
The exact same code in Vue 2.0 inserts a non-reactive object to the array:
http://jsbin.com/zuwihahiwa/1/edit?html,js,output
(Note that the newly added items are not live updated when edited)
<button #click="items.push({})">Add item</button>
I know that Vue inserts hooks on arrays when initialized, but being a Vue model binding that creates the new item's "name" property, I thought it could be automatically hooked like in Vue 1.
I find this new behavior very inconvenient, for it forces me to add a prototype of the object I want to add in Vue's data and clone it:
http://jsbin.com/bamasobuti/1/edit?html,js,output
In Vue's data:
item_prototype: {id: null, name: ""}
In the template:
<button #click="items.push(_.clone(item_prototype))">Add item</button>
My question is: Is there a recommended way of adding elements without having to keep a prototype of an empty element?
The change is not related to reactivity system change, but rather how v-model works in 2.0: it no longer magically creates non-existent paths and make them reactive for you.
This change is intended to force you to think about your data shape and to make your application state more predictable, similar to how you are expected to declare the reactive properties in your component's data option.
If I were you, I'd create a method and just do this.items.push({ id: null, name: '' }). There's no need to clone it.
According Vue's documentation, v-model="item.prop" is just syntactic sugar for:
v-bind:value="item.prop" v-on:input="item.prop = $event.target.value".
To make it work, just stop using
v-model="item.prop" and use this instead:
:value="item.prop" #input="$set(item,'prop',$event.target.value)"

Categories

Resources