Vue JS custom component v-model - javascript

Hello everyone i have a bit of a problem trying to get v-model to work on a custom component i've created. The problem is that this component consists of two inputs, and each time these are changed i emit the "input" event and bind it to an array i have on the parent.
<key-value-input v-for="n in inputs" v-model="provider.params"></key-value-input>
Then in the Component itself...
updateData() {
this.$emit('input', {
key: this.inputData.key,
value: this.inputData.value
})
}
This kinda works the problem is that it replaces provider.params from the original empty array into an object containing only one of the several key-value combinations i might have since this component can be duplicated at runtime...
So the question is, how do i make it so that v-model can fetch the data from each sub-component and simply set it as objects in an array on the parent?

If I understand you correctly, you can simply use v-model on the array element itself:
<key-value-input v-for="n in inputs" v-model="provider.params[n-1]"></key-value-input>
Here's the JSFiddle: https://jsfiddle.net/2be4maxm/

Related

How to render React component taken from an array of objects and pass props to it?

I have an array of objects, one of the properties of each object is a JSX element that I want to render based on an index that I keep as a state in the parent component. I also need to pass props to these components, so I thought I could do something like <MyArray[index].myJsx foo="bar" /> But this seems not to be a valid syntax (due to the index part, I guess). How could I accoplish that? I don't think Array.map() fits my use case, since I want to show these components one at a time

How v-model make a property of object reactive?

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.

can I pass a value to a sibling component without saving it as a variable?

I have some bootstrap vue cards with a v-for statement, one of my tags returns a value based on the current iteration of v-for. I want to pass this value to another tag, if I save it as a variable before passing it then all iterations end up sharing the same variables but I don't want that.
<b-card-group v-for="(element, i) in array" :key="index">
<b-card>
<tag #update="updateFunction()" />
<otherTag />
<b-card />
<b-card-group />
I want the variable to be specific to the current iteration so future iterations won't have access to it.
You are basically asking two questions, so I am going to answer them in turn.
The main problem you seem to have is that you do not know how to differentiate between each card in your updateFunction function. The easiest way to make your updateFunction context-aware, is by putting an arrow function that takes one argument, and calls updateFunction with two arguments.
Your update handler would look something like this. You take the index of the card you are trying to update, and the value that was sent from a sibling.
methods: {
handleUpdate(index, val) {
this.$set(this.cards, index, val);
}
}
You then use an arrow function to make the update handler context-aware:
<tag #update="(value) => handleUpdate(index, value)" />
As for the question in the title of your problem description: If you are passing a value as a prop, you have two options. You either put a literal in the prop (e.g. :my-prop="5"), or you put a variable in the prop (e.g. :my-prop="myVariable"). The first one obviously doesn't allow you to change the value, so that is not very useful here. The second one pulls the data directly from the state of the parent component. It is thus not possible to pass data as a prop without it being saved in the parent component.
As shown above, that doesn't mean you can't have different data for different iterations of your v-for though.
There are other methods of sharing data between siblings, which is especially useful in larger applications, by using a vuex store. This will allow you to abstract away data in useful "chunks" (or modules), allowing you to interact with it using getters, mutations and actions. You can find more information using the official documentation.

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