'v-model'指令无法更新迭代变量本身

时间:2019-09-17 12:43:23

标签: javascript vue.js

我读了一篇有关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模型似乎不起作用?

3 个答案:

答案 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

问题和论坛

https://forum.vuejs.org/t/v-model-and-slots/17616

https://github.com/vuejs/vue/issues/9726

答案 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>