I tried to use components inside v-for
loop and init the ref
to future access some methods of these from parent. Here a simplified code of my case:
<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>
And ListItem
component:
<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>
It's just renders a few list items and highlights one of them after one and half second. But I got an error: Uncaught TypeError: _this.$refs.item2.highlight is not a function
After debug session I've found an interesting fact: refs defined inside v-for
loop are not a components but the arrays with one component.
What is the logic, what is the f wrapper? Does anyone meet this case? Can somebody give the explanation of this behaviour?
Code presented above works fine with setTimeout(() => this.$refs.item2[0].highlight(), 1500);
Must I always pass [0]
? Is there exist a better way? Help, please.
Accessing the Refs Note that you can only access the ref after the component is mounted. If you try to access $refs.input in a template expression, it will be null on the first render. This is because the element doesn't exist until after the first render!
Ref s are Vue. js instance properties that are used to register or indicate a reference to HTML elements or child elements in the template of your application. If a ref attribute is added to an HTML element in your Vue template, you'll then be able to reference that element or even a child element in your Vue instance.
Unlike ref , reactive can only be initialized with an object. Each property of the object can be a different reactive variable, however. One advantage of reactive is that it doesn't use a value property so it may be a little easier to read. It also means it looks the same in JavaScript as in the template.
The v-bind directive is a Vuejs directive used to bind one or more attributes, or a component prop to an element. If that attribute is binded to our data defined in Vuejs instance then dynamically changes can be observed as data changes.
When using refs with v-for, the component / DOM nodes are stored as an array directly to the variable name so you don't need to use index number in the ref name. So you can do this:
<list-item v-for="item in items" :key="item.id" :value="item.text" ref="items" />
And use the refs in your component like this:
this.$refs.items[index]
Also note that the refs may not be in order and would need to be handled in a different way which is a completely different issue. You can follow that here: https://github.com/vuejs/vue/issues/4952
For Vue 3 users:
In Vue 3, such usage will no longer automatically create an array in $refs
. To retrieve multiple refs from a single binding, bind ref
to a function which provides more flexibility (this is a new feature):
HTML
<div v-for="item in list" :ref="setItemRef"></div>
With Options API:
export default { data() { return { itemRefs: [] } }, methods: { setItemRef(el) { if (el) { this.itemRefs.push(el) } } }, beforeUpdate() { this.itemRefs = [] }, updated() { console.log(this.itemRefs) } }
With Composition 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 } } }
Here is the doc link: https://v3.vuejs.org/guide/migration/array-refs.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With