'v-model' directives cannot update the iteration variable itself
My solution was very simple (see v-model="tags[index]"
):
Instead of doing this:
<template v-for="tag in tags">
<TagView :key="tag.key" v-model="tag" />
</template>
You should do this:
<template v-for="(tag, index) in tags">
<TagView :key="tag.key" v-model="tags[index]" />
</template>
The reason is you cannot pass iterated object tag
into v-model
for modifications. Please find more info about this: Iterating a list of objects with foreach
I think we shouldn't modify the passed data to a slot, pretty much like component props. However, I think it could be a bug.
1st approach
The v-model
directive works using a nested field in the passed data to the slot.
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<custom-component v-model="tags">
<div slot-scope="{tags,addTag,input}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" @keydown.enter.prevent="addTag" v-model="input.value">
</div>
</custom-component>
</div>
</body>
</html>
Vue.component('custom-component',{
props:['value'],
data(){
return {
input: {
value: ''
}
}
},
methods:{
addTag(){
this.$emit('input',[...this.value,this.input.value])
console.log([...this.value,this.input.value])
this.input.value = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
input:this.input
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
2nd approach
Use the input event to get the input value attribute directly
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<custom-component v-model="tags">
<div slot-scope="{tags,addTag}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" @keydown.enter.prevent="addTag">
</div>
</custom-component>
</div>
</body>
</html>
Vue.component('custom-component',{
props:['value'],
data(){
return {
newTag:''
}
},
methods:{
addTag(evt){
console.log(evt.target.value)
this.$emit('input',[...this.value, evt.target.value])
evt.target.value = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
newTag:this.newTag
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
You can also check these related issues:
StackOverflow
Using v-model inside scoped slots
Issues and Forum
https://forum.vuejs.org/t/v-model-and-slots/17616
https://github.com/vuejs/vue/issues/9726
Consider the following two JavaScript examples:
for (let value of array) {
value = 10
}
function (value) {
value = 10
}
In both cases trying to assign 10
to value
will only have an effect locally, it won't have any impact beyond the local scope. The caller, for example, would not be affected by the change.
Now consider these two examples where an object is used instead, where the object is of the form { value: 9 }
:
for (let valueWrapper of array) {
valueWrapper.value = 10
}
function (valueWrapper) {
valueWrapper.value = 10
}
In this case the change is not limited to the local scope as we're updating objects. External code, such as the caller of the function, would also be impacted by this change to the value
property as it can see the same object.
These examples are equivalent to trying to update a value using v-model
in a variety of cases. The first two examples are equivalent to:
<template v-for="value in array">
<input v-model="value">
</template>
and:
<template v-slot="{ value }">
<input v-model="value">
</template>
The arguments passed to v-slot
can very much be thought of as analogous to function parameters. Neither the loop nor the scoped slot would work as desired, exactly the same as they don't for their pure JavaScript equivalents.
However, the latter two of my four examples would be equivalent to:
<template v-for="valueWrapper in array">
<input v-model="valueWrapper.value">
</template>
and:
<template v-slot="{ valueWrapper }">
<input v-model="valueWrapper.value">
</template>
These should work fine as they are updating a property on an object.
However, to go back to the original question, it's important that we're binding the appropriate object. In this case we would need to bind the newTag
property of the component. Copying that property to another object wouldn't work either as v-model
would just be updating an irrelevant object.