【问题标题】:'v-model' directives cannot update the iteration variable itself“v-model”指令不能更新迭代变量本身
【发布时间】:2019-09-17 12:43:23
【问题描述】:

我读过一篇关于Renderless Components 的文章,它通过 $scopedSlots 属性将一个组件拆分为一个展示组件(view 部分)和一个无渲染组件(logical 部分) .这是一个简单的标签组件。当你按 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-model。 jsbin上的demo

<!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-model 不起作用。

编辑

上面的问题已经回答清楚了,而我在阅读参考链接后得到另一个问题,仍然是 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-model 似乎不起作用?

【问题讨论】:

  • 这个问题的代码量很大,很难理解。

标签: javascript vue.js


【解决方案1】:

我的解决方案非常简单(参见v-model="tags[index]"):

不要这样做:

<template v-for="tag in tags">
    <TagView :key="tag.key" v-model="tag" />
</template>

你应该这样做:

<template v-for="(tag, index) in tags">
    <TagView :key="tag.key" v-model="tags[index]" />
</template>

原因是您不能将迭代对象tag 传递给v-model 进行修改。请查找有关此的更多信息:Iterating a list of objects with foreach

【讨论】:

  • 这是一个比公认的答案短得多但有用的答案。
【解决方案2】:

考虑以下两个 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 只会更新一个不相关的对象。

【讨论】:

    【解决方案3】:

    我认为我们不应该将传递的数据修改为插槽,就像组件道具一样。但是,我认为这可能是一个错误。

    第一种方法

    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'
        ]
      }
    })
    

    第二种方法

    使用输入事件直接获取输入值属性

    <!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'
        ]
      }
    })
    

    您还可以查看这些相关问题:

    堆栈溢出

    Using v-model inside scoped slots

    问题和论坛

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

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

    【讨论】:

      【解决方案4】:

      请使用 Vuejs Object 的 Ref 为您的解决方案,这里为我使用 refence 和 testing,使用 tailwind、vue3 作为独立组件。

      记得评价这对我很有帮助

      // import function ref extiende of object
      // importamos la funcion ref que extiende objeto vue
      import { ref } from 'vue';
      
      export default {
        data() {
          return {
            // create refence with object for change
            // creamos una referencia con los objetos que van a cambiar
            fruits: ref({
              oranges: {
                name: 'naranjas',
                value: false
              },
              apple: {
                name: 'manzanas',
                value: false
              },
              pear: {
                name: 'peras',
                value: false
              }
            })
          };
        }
      };
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
      <template>
        <div>
          <!-- use tailwindcss vue -->
          <div class="w-2xl bg-blue-50 text-gray-400 font-bold">
            <!-- iteration is here -->
            <div v-for="fruit in fruits" :key="fruit">
              <label for="">{{ fruit.name }}</label>
              <!-- this can be a component example: @headlessui/vue-->
              <input v-model="fruit.value" type="checkbox">
            </div>
          </div>
          <div class="w-2xl bg-blue-50 text-gray-400 font-bold">
            {{fruits}}
          </div>
        </div>
      </template>

      我尝试了很多方法来更改 v-model,但只是通过引用 javascript 对象没有任何效果

      【讨论】:

        猜你喜欢
        • 2018-09-17
        • 2022-08-18
        • 2021-08-21
        • 2018-06-26
        • 1970-01-01
        • 2021-02-12
        • 2019-04-30
        • 1970-01-01
        • 2018-08-24
        相关资源
        最近更新 更多