Exploring advanced use of template refs in Vue
21 Dec 2023
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:
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.)
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.
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.
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:
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!