Template Refs in Vue.js
Sometimes while creating components, we need to access its child components or child HTML elements directly in the JavaScript. Though we are having props and events for creating communication with child components, Vue.js provides the special attribute ref
. We can create a reference variable using the ref
attribute for child components/elements.
During the virtual DOM mounting process, if the V-Node(HTML element or child component) has a ref
attribute then the respected element or component instance will be stored under the parent component’s $refs
. Let’s take a deep dive into this with an example. Read Components in Vue3 and Single File Components Vue 3 if you are new to Vue.js components.
Template Refs in Vue.js
We can use ref
to create the reference for the element or child component. If ref
is used on the HTML DOM element then the reference is mapped to the respected element. If ref
is used on the child component, then the reference is mapped to the component instance. The parent component $refs
object, stores the ref reference.
- Attribute Name :
ref
- Expects : string or function
- Example : <input ref=”inputId” />
Example
<!-- vm.$refs.p will be the HTML DOM element -->
<p ref="p">hello to program easily</p>
<!-- vm.$refs.child will be the child component instance -->
<child-component ref="child"></child-component>
<!-- When bound dynamically, we can define ref as a callback function,
passing the element or component instance explicitly -->
<child-component :ref="(el) => child = el"></child-component>
However ref
registration timing is very important. Firstly these processes are done after the component render operation. So we can not access it on the initial render. Secondly we can not use $refs
inside the template and data binding since it is not reactive. Now we are going to create the base-input component and use ref
as follows,
In the above example, we need to call the updateInput method in the onMounted
hook. Here the input element is available. We can access them using this.$refs.input
. We have to add the input-comp in the index.html like below.
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<input-comp />
</div>
<script src="src/index.js"></script>
</body>
</html>
Template Refs in Vue.js – Composition API
We can integrate the two concepts Template Refs and Reactive Refs in the Composition API. Generally we are using $refs
to access the component instance/element. But here we can access the component instance/element with the help of reactive ref
.
<template>
<div ref="root">Welcome to ProgramEasily</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const root = ref(null);
onMounted(() => {
// the DOM element will be assigned to the ref after initial render
console.log(root.value) // <div>Welcome to ProgramEasily </div>
});
return {
root
}
}
}
</script>
In the above example, root is the reactive ref
object which returned from the setup function. During the Virtual DOM mount operation, if the V-Node key matches with the ref
of the render context, then the component instance/element is assigned to the value of the ref
. In detail here,
- V-Node : <div ref=”root”>Welcome to ProgramEasily</div>
- V-Node key : root (ref=”root”)
- ref of the render context : i.e. return from the setup function(const root = ref(null))
Indeed, V-Node key root matches with ref
of the render context. So we can access the element after the initial render. We can get the element in the onMounted
Lifecycle hook like in the example.
Template Refs – Watching
watch
and watchEffect
are run before the onMounted
Lifecycle hook. But the DOM element will be available in the mounted phase. The result is template ref element not available on the watch operation. Refer the below example, here the root.value is null.
<template>
<div ref="root">Welcome to ProgramEasily</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
const root = ref(null);
watchEffect(() => {
// called before the DOM is mounted or updated
console.log(root.value) // => null
})
return {
root
}
}
}
</script>
To solve this problem, Vue.js provides another option flush: 'post
. This will execute the watch effect after the DOM element mount/update operation. So that the V-Node key root matches with ref
of the render context and we can get the result as follows,
<template>
<div ref="root">Welcome to ProgramEasily</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
const root = ref(null);
watchEffect(() => {
console.log(root.value) // => <div>...</div>
},
{
flush: 'post'
});
return {
root
}
}
}
</script>
References
- https://v3.vuejs.org/guide/component-template-refs.html
- https://v3.vuejs.org/guide/composition-api-template-refs.html#template-refs
- https://v3.vuejs.org/api/special-attributes.html#ref
Summary
- Template
refs
used to create the references for the element or child component. - The parent component
$refs
object, stores theref
reference. - In the Composition API, Template
refs
matched to Reactiverefs
in the Virtual DOM mount operation. - When we watch Template
refs
, we have to addflush: 'post
to watch theref
after the DOM updated.