J'ai essayé d'utiliser des composants dans la boucle v-for
Et d'initier le ref
pour accéder ultérieurement à certaines méthodes de ceux-ci depuis le parent. Voici un code simplifié de mon cas:
<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>
Et le composant 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>
Il rend juste quelques éléments de liste et met en évidence l'un d'eux après une seconde et demie. Mais j'ai une erreur: Uncaught TypeError: _this.$refs.item2.highlight is not a function
Après la session de débogage, j'ai trouvé un fait intéressant: les références définies dans la boucle v-for
Ne sont pas des composants mais les tableaux avec un composant.
Quelle est la logique, quel est le wrapper f? Quelqu'un rencontre-t-il ce cas? Quelqu'un peut-il expliquer ce comportement?
Le code présenté ci-dessus fonctionne correctement avec setTimeout(() => this.$refs.item2[0].highlight(), 1500);
Dois-je toujours passer [0]
? Existe-t-il une meilleure façon? Aidez-moi, s'il vous plaît.
Lorsque vous utilisez des références avec v-for, les nœuds composant/DOM sont stockés sous forme de tableau directement dans le nom de la variable, vous n'avez donc pas besoin d'utiliser le numéro d'index dans le nom de la référence. Vous pouvez donc faire ceci:
<list-item
v-for="item in items"
:key="item.id"
:value="item.text"
ref="items"
/>
Et utilisez les références dans votre composant comme ceci:
this.$refs.items[index]
Notez également que les références peuvent ne pas être en ordre et devraient être traitées d'une manière différente, ce qui est un problème complètement différent. Vous pouvez suivre cela ici: https://github.com/vuejs/vue/issues/4952
J'avais fait face au même problème.
Comme sobolevon l'a mentionné, la valeur de retour de $refs.{ref name}
Est un tableau dans les références v-for, donc ma solution est de considérer que $refs.{ref name}
Est un tableau avec un seul élément par défaut et d'écrire la fonction $refs.{ref name}[0].methodToCall()
.
Et cela fonctionne pour mon cas.
Compte tenu de votre question principale: https://vuejs.org/v2/api/#ref
La documentation dit:
Lorsque ref est utilisé avec v-for, la référence que vous obtenez sera un tableau contenant les composants enfants reflétant la source de données.
Mais, je dirais que vous vous trompez, car l'utilisation de refs
n'est pas une bonne façon de procéder. Nous avons des alternatives très utiles dans vue
- land. Par exemple, on peut utiliser un prop
.
Voilà à quoi ressemblerait une version réécrite de votre code:
<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>
Et puis changez la définition de votre composant pour recevoir un nouveau prop
:
<script>
export default {
name: "list-item",
props: ["value", "isHighlighted"]
};
</script>
Cela résoudra votre problème.