Template Refs

Vue makes the page's elements for you, so you rarely need them yourself. But some things can only be done to an element: putting the cursor in a text field with focus(), scrolling to it, starting a video. For those, you need the element itself, as querySelector() gave it to you in the JavaScript course.

In Vue, you mark the element in the template with the attribute ref and a name of your choice, and you get it in the script with useTemplateRef() and the same name:

<script setup>
import { onMounted, useTemplateRef } from "vue";

const note = useTemplateRef("note");

onMounted(() => {
    note.value.focus();
});
</script>

<template>
    <label>
        A note for the barista
        <textarea ref="note"></textarea>
    </label>
</template>

note is a template ref: a ref whose .value is the element, here the textarea itself, with everything you know from the DOM: focus(), value, classList… When the app starts, the cursor is in the field, and the customer can type straight away. (In this exercise, that's where your cursor goes too when you click Run: click into the editor to go on with your code.)

When can you use it? Only once the component is mounted. Before that, the element doesn't exist yet, and note.value is null. That's why focus() is in onMounted. In a handler of an event, such as a click, the element is there too: a button can only be clicked once it's on the page.

Why not document.querySelector("textarea")? It searches the whole page and finds the first textarea, which may belong to another component. A template ref is always the element of this component's own template.

useTemplateRef() is new in Vue 3.5. In older code, you'll see const note = ref(null); together with ref="note", the same name as the variable's, which works the same way.

A typical mistake: different names. With ref="note" in the template and useTemplateRef("notes") in the script, note.value stays null, and note.value.focus() ends with TypeError: Cannot read properties of null (reading 'focus').

Instructions

  1. When the app starts, put the cursor in the search field: give the field ref="search", get it with useTemplateRef("search"), and call its focus() in onMounted.

  2. After the field, add a button Clear that empties the search and puts the cursor back in the field, so the customer can type something else straight away.