我读了一篇有关Renderless Components的文章,该文章通过$将一个组件分为一个呈现组件(视图部分)和一个非渲染组件(逻辑部分)。 scopedSlots属性。这里是一个简单的Tag组件。当您按Enter时,您将添加一个新标签
<!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,newTag}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" @keydown.enter.prevent="addTag" v-model="newTag">
</div>
</custom-component>
</div>
<script>
Vue.component('custom-component',{
props:['value'],
data(){
return {
newTag:''
}
},
methods:{
addTag(){
this.$emit('input',[...this.value,this.newTag])
this.newTag = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
newTag:this.newTag
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
</script>
</body>
</html>
但是,它不起作用,看来newTag始终是”(空字符串),当我使用SPA方式时, 模拟器说“'v-model'指令无法更新迭代变量'newTag'本身”,这里是demo on jsbin
解决方案是如本文中所述,使用:value属性绑定和@input事件绑定,而不是v模型。 jsbin上的演示
<!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,inputAttrs,inputEvents}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" v-bind="inputAttrs" v-on="inputEvents">
</div>
</custom-component>
</div>
<script>
Vue.component('custom-component',{
props:['value'],
data(){
return {
newTag:''
}
},
methods:{
addTag(){
this.$emit('input',[...this.value,this.newTag])
this.newTag = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
inputAttrs:{
value:this.newTag
},
inputEvents:{
input:(e) => {
this.newTag = e.target.value
},
keydown:(e) => {
if(e.keyCode === 13){
e.preventDefault()
this.addTag()
}
}
}
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
</script>
</body>
</html>
我不知道为什么v模型不起作用。
编辑
上面的问题已经清楚地回答了,虽然我在阅读参考链接后又遇到了另一个问题,但是v-model仍然无法正常工作
<!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">
<base-test v-slot="sp">
<input type="text" v-model="sp.foo">
<div>{{ sp}}</div>
</base-test>
</div>
<script>
Vue.component('base-test', {
template: `
<div>
<slot :foo="foo"></slot>
</div>
`,
data(){
return{
foo: 'Bar',
}
}
});
// Mount
new Vue({
el: '#app',
});
</script>
</body>
</html>
我们可以看到,sp是一个对象。为什么这次v模型似乎不起作用?
答案 0 :(得分:3)
请考虑以下两个JavaScript示例:
for (let value of array) {
value = 10
}
function (value) {
value = 10
}
在两种情况下,尝试将10
分配给value
只会在本地产生影响,而不会超出本地范围。例如,呼叫者将不受更改的影响。
现在考虑这两个示例,其中使用一个对象代替,该对象的形式为{ value: 9 }
:
for (let valueWrapper of array) {
valueWrapper.value = 10
}
function (valueWrapper) {
valueWrapper.value = 10
}
在这种情况下,更改不限于本地范围,因为我们正在更新对象。外部代码(例如函数的调用者)也会因为对value
属性的更改而受到影响,因为它可以看到同一对象。
这些示例等效于在各种情况下尝试使用v-model
更新值。前两个示例等效于:
<template v-for="value in array">
<input v-model="value">
</template>
和:
<template v-slot="{ value }">
<input v-model="value">
</template>
传递给v-slot
的参数可以非常类似于函数参数。循环和作用域插槽都无法按需工作,与纯JavaScript等效项完全不同。
但是,我的四个示例中的后两个等同于:
<template v-for="valueWrapper in array">
<input v-model="valueWrapper.value">
</template>
和:
<template v-slot="{ valueWrapper }">
<input v-model="valueWrapper.value">
</template>
这些对象在更新对象的属性时应该可以正常工作。
但是,回到最初的问题,重要的是我们要绑定适当的对象。在这种情况下,我们将需要绑定组件的newTag
属性。将该属性复制到另一个对象上也不会起作用,因为v-model
只会更新不相关的对象。
答案 1 :(得分:1)
我认为我们不应该将传递的数据修改为插槽,就像组件道具一样。但是,我认为这可能是一个错误。
第一种方法
v-model
指令使用传递到插槽中的数据中的嵌套字段来工作。
<!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'
]
}
})
第二种方法
使用input事件直接获取输入值属性
<!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'
]
}
})
您还可以检查以下相关问题:
StackOverflow
Using v-model inside scoped slots
问题和论坛
答案 2 :(得分:0)
我的解决方案非常简单(参见v-model="repParams[index]"
):
而不是这样做:
<template v-for="repParam in reportParams">
<SubscriptionParamView :key="repParam.key" v-model="repParam" />
</template>
你应该这样做:
<template v-for="(repParam, index) in reportParams">
<SubscriptionParamView :key="repParam.key" v-model="reportParams[index]" />
</template>