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

src/views/Gallery.vue
<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!

src/views/Gallery.vue
<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>

Further reading: