v-for循环内的Vue.js ref

时间:2018-08-29 21:10:48

标签: javascript vue.js vuejs2 vue-component

我尝试在v-for循环中使用组件,并初始化ref以便将来从父级访问其中的某些方法。这是我的情况的简化代码:

<template>
    <div class="hello">
        {{ msg }}
        <ul>
            <list-item 
                v-for="item in items" 
                :key="item.id" 
                :value="item.text" 
                :ref="`item${item.id}`"
            />
        </ul>
    </div>
</template>

<script>
    import ListItem from "./ListItem";
    export default {
        name: "HelloWorld",
        components: {
            ListItem
        },
        data() {
            return {
                msg: "Welcome to Your Vue.js App",
                items: [
                    { id: 1, text: "foo" },
                    { id: 2, text: "bar" },
                    { id: 3, text: "baz" },
                    { id: 4, text: "foobar" }
                ]
            };
        },
        mounted() {
            setTimeout(() => this.$refs.item2.highlight(), 1500);
        }
    };
</script>

还有ListItem组件:

<template>
    <li v-bind:class="{ highlight: isHighlighted }">
        {{value}}
    </li>
</template>

<script>
    export default {
        name: "list-item",
        props: ["value"],
        data() {
            return {
                isHighlighted: false
            };
        },
        methods: {
            highlight() {
                this.isHighlighted = !this.isHighlighted;
            }
        }
    };
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    .highlight {
        color: red;
    }
</style>

它只是呈现一些列表项,并在半秒后突出显示其中一个。但是我遇到了一个错误:Uncaught TypeError: _this.$refs.item2.highlight is not a function
在调试会话之后,我发现了一个有趣的事实:v-for循环中定义的引用不是组件,而是具有一个组件的数组。
逻辑是什么,f包装器是什么?有人遇到这种情况吗?有人可以解释这种行为吗?
上面提供的代码可以与setTimeout(() => this.$refs.item2[0].highlight(), 1500);配合使用 我是否必须始终通过[0]?有没有更好的方法?请帮助。

7 个答案:

答案 0 :(得分:5)

我试图通过传递方法中的索引来处理v-for内部的引用:

<div v-for="(item, index) in items" @click="toggle(index)">
  <p ref="someRef"></p>
</div>

toggle(index) {
  this.refs['someRef'][index].toggle();
}

但是实际上,由于ref的索引未排序,因此切换了错误的元素。

所以我所做的是将数据属性添加到ref元素:

<div v-for="(item, index) in items" @click="toggle(index)">
  <p ref="someRef" :data-key="index"></p>
</div>

现在每个引用都有其特定的数据键。可以这样切换:

toggle(index) {
  const dropdown = this.$refs['someRef'].find(
        el => el.$attrs['data-key'] === index
    );
  dropdown.toggle();
}

答案 1 :(得分:4)

对于 Vue 3 用户:

在 Vue 3 中,此类用法将不再自动在 $refs 中创建数组。要从单个绑定中检索多个引用,请将 ref 绑定到提供更多灵活性的函数(这是一项新功能):

HTML

<div v-for="item in list" :ref="setItemRef"></div>

使用选项 API:

export default {
  data() {
    return {
      itemRefs: []
    }
  },
  methods: {
    setItemRef(el) {
      if (el) {
        this.itemRefs.push(el)
      }
    }
  },
  beforeUpdate() {
    this.itemRefs = []
  },
  updated() {
    console.log(this.itemRefs)
  }
}

使用合成 API:

import { onBeforeUpdate, onUpdated } from 'vue'

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el => {
      if (el) {
        itemRefs.push(el)
      }
    }
    onBeforeUpdate(() => {
      itemRefs = []
    })
    onUpdated(() => {
      console.log(itemRefs)
    })
    return {
      setItemRef
    }
  }
}

这是文档链接:https://v3.vuejs.org/guide/migration/array-refs.html

答案 2 :(得分:1)

我也遇到过同样的问题。

如sobolevon所述,$refs.{ref name}的返回值是v-for refs中的数组,因此我的解决方案是将$refs.{ref name}视为默认情况下仅包含一项的数组,并写入{{ 1}}。

它适用于我的情况。

答案 3 :(得分:1)

我通过使用动态引用解决了排序问题::ref="'myRef' + index"

如果这样做,Vue 会为 v-for 中的每个项目创建一个新数组,其中唯一的元素将始终是您想要的 ref。然后您可以使用 this.$refs['myRef' + index][0] 访问它。

(这在 Vue 3 中不起作用。)

答案 4 :(得分:0)

考虑您的主要问题:https://vuejs.org/v2/api/#ref

文档说:

  

将ref与v-for一起使用时,获得的ref将是一个数组,其中包含镜像数据源的子组件。

但是,我会说您做错了,因为使用refs并不是一个好方法。在vue地区,我们有非常有用的替代方法。例如,可以使用prop

这就是您的代码的重写版本:

<template>
    <div class="hello">
        {{ msg }}
        <ul>
            <list-item 
                v-for="item in items" 
                :key="item.id" 
                :value="item.text" 
                :isHighlighed="item.isHighlighed"
            />
        </ul>
    </div>
</template>

<script>
    import ListItem from "./ListItem";
    export default {
        name: "HelloWorld",
        components: {
            ListItem
        },
        data() {
            return {
                msg: "Welcome to Your Vue.js App",
                items: [
                    // We have moved `isHighlighed` falg into the data array:
                    { id: 1, text: "foo", isHighlighed: false },
                    { id: 2, text: "bar", isHighlighed: true },
                    { id: 3, text: "baz", isHighlighed: false },
                    { id: 4, text: "foobar", isHighlighed: false }
                ]
            };
        };
    };
</script>

然后更改您的组件定义以接收新的prop

<script>
    export default {
        name: "list-item",
        props: ["value", "isHighlighted"]
    };
</script>

这将解决您的问题。

答案 5 :(得分:0)

在v-for中使用引用时,组件/ DOM节点直接作为数组存储在变量名中,因此您无需在引用名中使用索引号。因此,您可以这样做:

<list-item
  v-for="item in items" 
  :key="item.id" 
  :value="item.text" 
  ref="items"
/>

并像这样使用组件中的引用:

this.$refs.items[index]

还请注意,引用可能不正确,需要以不同的方式处理,这是完全不同的问题。您可以在此处进行以下操作:https://github.com/vuejs/vue/issues/4952

答案 6 :(得分:0)

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.21/vue.js"></script>
<div 
   v-for="(item,index) in items"
   :key="index">
<q-popup-proxy
   ref="qDateProxy">
  <q-date
     :data-key="index"
     v-model="item.date"
     @input="CalendarHide(index)"
     mask="MM/DD/YYYY"
    range>
  </q-date>
</q-popup-proxy>
</div>

<script>
CalendarHide (Val) {
      this.$refs ['qDateProxy'] [val].hide()
}
</script>