Template Refs in Vue.js

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.

Template Refs in Vue.js

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

Summary

  • Template refs used to create the references for the element or child component.
  • The parent component $refs object, stores the ref reference.
  • In the Composition API, Template refs matched to Reactive refs in the Virtual DOM mount operation.
  • When we watch Template refs, we have to add flush: 'post to watch the ref after the DOM updated.

Leave a Reply

%d bloggers like this: