Template refs on v-for
Introduction
Sometimes, we need to access a DOM element from a template directly, i.e. in order to apply a plugin or library that isn't built for Vue specifically. for this occasion, we have template refs:
<template>
<div ref="someName">
<template>
<script>
export default {
mounted() {
console.log(this.$refs.someName)
}
}
</script>
This works fine in Vue 3 as well, but for internal implementation reasons, it no longer does in the same way for refs you put on elements that also have a v-for
directive. So now we will migrate code that uses this:
In Vue 2
<li
v-for="cat in cats"
:key="cat.id"
ref="list"
>
<ImageCard :image="cat.url" :title="`Cat, ID ${cat.id}`" />
</li>
<script>
export default {
// ...
mounted() {
// this.$refs.list would give you an array containing all of the elements in that v-for loop.
console.log('template refs from a v-for: ', this.$refs.list)
},
updated() {
console.log('template refs from a v-for: ', this.$refs.list)
},
// ...
}
</script>
Migrating to Vue 3
The simple ref="stringName"
syntax doesn't support v-for
loops anymore, but ref
now has an alternate syntax where we can bind a function to it and basically do anything with the element(s) the ref applies to.
In order to use that on elements with v-for, we need an array that we can push these elements into, and we also need to re-set this array before each render update.
Then we can read the elements from this array instead of this.$refs.listElements
. The upside is that this array can also be reactive, and now we got reactive template refs, which were not possible in Vue 2!
<li
v-for="cat in cats"
v-bind:ref="(el) => listElements.push(el)"
>
<ImageCard :image="cat.url" :title="`Cat, ID ${cat.id}`" />
</li>
<script>
export default {
// ...
data: () => ({
// ...
listElements: []
}),
beforeUpdate() {
// reset the array before each re-render:
this.listElements.length = 0
},
mounted() {
console.log('template refs from a v-for: ', this.listElements)
},
updated() {
console.log('template refs from a v-for: ', this.listElements)
},
// ...
}
</script>