Exploring advanced use of template refs in Vue

Exploring advanced use of template refs in Vue

21 Dec 2023 vue

In this article I'll take a look at some advanced usage of template refs in Vue, specifically their use:

  • Inside v-for loops
  • With callback functions
  • On references to child components

Quick refresher on template refs 🔗

Vue's template refs, in their most basic form, allow you to gain access to native DOM elements. Normally, this is not necessary, since the nature of reactive frameworks is such that you rarely need to talk to the DOM elements manually - indeed, doing so runs the risk of disrupting Vue's internal manipulation and tracking of the DOM. However, they can sometimes be useful.

Vue provides the ref attribute, like so:

<input ref='myField' />

With that in place, in our component's setup script we can access the element via ref() - the same ref() we use to make a piece of data reactive.

<script setup> import { ref, onMounted } from 'vue' const myField = ref(null); onMounted(() => myField.value.focus()); </script>

A couple of important notes, there.

  • We can access our element only once the component has mounted (the equivalent to waiting for the DOMContentLoaded event to fire in normal DOM-scripting).
  • The name of the variable to which we assign the ref must match the attribute value we used ("myField"). This is how Vue ties together the attribute to our ref-storing variable (myField).

With this example I'm using Vue 3's setup script, but here's how the above works if you're instead using the older-style setup function.

Using refs inside a v-for loop

A v-for is used to iteratively output an element or component multiple times via a loop. How, then, could we use template refs with loops, and what would our corresponding variable point to?

Vue's approach to refs inside loops is quite clever. Everything is mostly the same, except our ref variable should be initialised to an array (rather than null, as before.)

<div v-for='i in foo' ref='myDivs'>...</div>

And over in our JS:

<script setup> import { ref, onMounted } from 'vue' const myDivs = ref([]); onMounted(() => myDivs.value.forEach(div => div.style.background = 'red') ); </script>

So this Vue auto-populates our variable with an array of elements, not just one.

Using callback functions with template refs 🔗

So far in our ref attributes we've stored the name of a variable. But it's also possible to pass a function (or a reference to a function) for greater control over how our element(s) are handled and where we store references to them.

<input :ref='el => myOtherRef = el' value='foo' >

And then in our JS:

<script setup> import { ref, onMounted } from 'vue' const myOtherRef = ref(); onMounted(() => alert(myOtherRef.value.value); //"foo" ); </script>

Vue will call our callback function each time the component is updated.

Using template refs on child components 🔗

Refs get really interesting when used on references to child components, because it's via this means that we can access data exposed by the child.

From the parent perspective, everything looks like we saw before.

<some-child-component ref='foo' />

And the parent JS:

<script setup> import { ref, onMounted } from 'vue' const child = ref(null); onMounted(() => alert(child.value.foo)); </script>

And here's our child component JS:

<script setup> defineExpose({ foo: 'bar' }); </script>

defineExpose() is one of those functions which, like defineProps() and several others, are automatically available inside setup scripts and thus don't need to be imported from Vue.

With this setup, our parent component's alert() would give "bar", as that's the value of the property exposed by the child.

If the child component is using the older-style setup function rather than setup script, things work a little differently; in such cases, our child ref will store a reference to the child component's this context.

This is best avoided, as it means your parent and child components would be closely coupled, which undermines Vue's preferred props-and-emits methodology.

Did I help you? Feel free to be amazing and buy me a coffee on Ko-fi!