Unlocking $refs with the Composition API in Vue3 - A step-by-step guide

I am currently exploring how to access $refs in Vue 3 using the Composition API. In my template, I have two child components and I specifically need to obtain a reference to one of them:

<template>
    <comp-foo />
    <comp-bar ref="table"/>
</template>

When working with Template Refs, the ref attribute is crucial as it allows us to directly access a specific DOM element or child component instance once it has been mounted.

In my code, everything works perfectly fine when utilizing the Options API:

  mounted() {
    console.log("Mounted - ok");
    console.log(this.$refs.table.temp());
  }

However, when attempting to achieve the same result using the Composition API, I encounter an error:

setup() {
    const that: any = getCurrentInstance();
    onMounted(() => {
      console.log("Mounted - ok");
      console.log(that.$refs.table.temp());//ERROR that.$refs is undefined
    });
    return {};
  }

If anyone can provide guidance on how to achieve this using the Composition API, it would be greatly appreciated.

Answer №1

To access the table element in the html, you must first create the ref constant inside the setup function and then return it.

<template>
    <div ref="table"/>
</template>

import { ref, onMounted } from 'vue';

setup() {
    const tableRef = ref(null);

    onMounted(() => {
      console.log(tableRef.value);
    });

    return { tableRef };
}

Answer №2

Working with Laravel Inertia:

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

// a test list
let items = [
  { id: 1, name: "item name 1" },
  { id: 2, name: "item name 2" },
  { id: 3, name: "item name 3" },
];

// referencing elements
let elements = ref(null);

// testing
onMounted(() => {
    
  let all = elements.value;
  let item1 = all[0];
  let item2 = all[1];
  let item3 = all[2];

  console.log([all, item1, item2, item3]);

});
</script>
<template>
  <div>

    <!-- elements -->
    <div v-for="(item, i) in items" ref="elements" :key="item.id">

      <!-- element's content -->
      <div>ID: {{ item.id }}</div>
      <div>Name: {{ item.name }}</div>

    </div>

  </div>
</template>

Answer №3

<template>
    <custom-table ref="table"/>
    ...
</template>

<script>
import { ref, onMounted } from 'vue';

setup() {
    const table = ref(null);

    onMounted(() => {
      table.value.addEventListener('click', () => console.log("Event occurred"))
    });

    return { table };
}
</script>

Within another component, you can interact with events that were registered on the onMounted lifecycle hook. In my example, I have only registered one event.

Answer №4

If desired, you have the option to utilize getCurrentInstance() within the parent component as demonstrated in the following code snippet:

<template>
    <MyCompo ref="table"></MyCompo>
</template>

<script>
import MyCompo from "@/components/MyCompo.vue"
import { ref, onMounted, getCurrentInstance } from 'vue'
export default {
    components : {
        MyCompo
    },
    
    setup(props, ctx) {
        
        onMounted(() => {
          getCurrentInstance().ctx.$refs.table.tempMethod()
           
        });
    }
}
</script>

Below is the code for the child component (referred to as MyCompo):

<template>
    <h1>this is MyCompo component</h1>
</template>

<script>
export default {
    
    setup(props, ctx) {
        
        const tempMethod = () => {
            console.log("temporary method");
        }

        return {
            tempMethod
        }
    },
}
</script>

Answer №5

If you're working with Vue 3 and the composition API, utilizing Template Ref allows for this functionality.

After the initial render, you can access the DOM since script setup runs before DOM creation. By using the onMounted hook, you can target elements accordingly.

For further information on template Ref, check out this informative blog post: https://medium.com/@plabonislam/how-to-access-dom-element-using-vue-3-compostion-api-2181d69abee6


<script setup>
 import { onMounted, ref } from "vue";
 const headline=ref(null);
 onMounted(()=>{
 console.log(headline.value) 
 
 }
</script>

<template>
 <h1 ref="headline"> hello world </h1>
</template>

Answer №6

In my experience, I encountered an issue where the ref variable was not binding to the component due to it not being rendered yet. To address this, I will be expanding upon @dantheman's solution. Take a look at the following example:

<template>
    <div v-if="false">
        <div ref="table"/>
    </div>
</template>

import { ref, onMounted } from 'vue';

setup() {
    const table = ref(null);

    onMounted(() => {
      console.log(table.value);
    });

    return { table };
}

In the above scenario, where the

<div ref="table"/>
is not rendered due to the condition, the const table remains as null. If the condition were to change from false to true, then the const table would be assigned a value. This behavior is also explicitly mentioned in the Vue.js documentation here:

Keep in mind that you can only access the ref after the component has been mounted. If you attempt to access the input in a template expression, it will be null during the initial render because the element does not exist until after the first render!

Therefore, it is crucial to not only consider the onMounted hook but also ensure that the component to which the ref is linked has been successfully mounted.

Answer №7

Perhaps using TypeScript could simplify this task. I found success with the following code:

const tableRef = ref<HTMLDivElement | null>(null);

Answer №8

When dealing with refs in an array, a previous answer mentioned the issue of logging an empty refs array in Vue versions between 3.2.25 and 3.2.31:

const {ref, onMounted} = Vue;

Vue.createApp({
  setup() {
    const items = [
      {id: 1, name: "item name 1"},
      {id: 2, name: "item name 2"},
      {id: 3, name: "item name 3"},
    ];
    const elements = ref([]);
    onMounted(() => {
      console.log(
        elements.value.map(el => el.textContent)
      );
    });
    return {elements, items};
  }
}).mount("#app");
<div id="app">
  <div v-for="(item, i) in items" ref="elements" :key="item.id">
    <div>ID: {{item.id}}</div>
    <div>Name: {{item.name}}</div>
  </div>
</div>

<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1f696a7a5f2c312d31505b505d32">[email protected]</a>/dist/vue.global.prod.js"></script>

To solve this issue, replacing refs="elements" with

:ref="el => elements[i] = el"
can be used as a workaround:

const {ref, onMounted} = Vue;

Vue.createApp({
  setup() {
    const items = [
      {id: 1, name: "item name 1"},
      {id: 2, name: "item name 2"},
      {id: 3, name: "item name 3"},
    ];
    const elements = ref([]);
    onMounted(() => {
      console.log(
        elements.value.map(el => el.textContent)
      );
    });
    return {elements, items};
  }
}).mount("#app");
<div id="app">
  <div v-for="(item, i) in items"
       :ref="el => elements[i] = el"
       :key="item.id">
    <div>ID: {{item.id}}</div>
    <div>Name: {{item.name}}</div>
  </div>
</div>

<script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e3959686a3d0cdd1cddadf4843444bd8">[email protected]</a>/dist/vue.global.prod.js"></script>

Answer №9

When utilizing the defineComponent method that is Typescript compatible, you have the option to delegate the required code to a function that does have access to this.$refs:

export default defineComponent({
  name: 'ModeBar',
  methods: {
    registerToolTips(): boolean {
      //this.$refs is available!!!
    },
  },
  mounted() {
    this.$nextTick(() => this.registerToolTips());
  },
});

It's important to note that using this.$nextTick() may not always be necessary and might not suffice in some cases. There is no guarantee that all elements will be fully rendered on mounted. If the desired $refs are not yet rendered, you may need to implement an interval within the mounted function until they become available, then deactivate it once they are located;

This explains why the registerToolTips function returns a boolean - to allow for retrying if initial execution fails.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Saving a JSON object to a .json file using JavaScript

let project = { Name : "xyz", Roll no 456 }; What is the best way to save the data stored in the project object to a .json file using JavaScript? ...

A neutral-toned backdrop that briefly shows up for a quick 7-second interlude during a background-image

Recently I created a website. On this website, there is a feature where 3 images change every 7 seconds for a total of 3 cycles. The code snippet used for this functionality is as follows: var swap; function run(interval, frames) { var int = 1; ...

How can you set a predetermined value for a dropdown menu after populating it with data from a Django database?

I currently have a query in my Views.py file that is used to populate a dropdown menu. The query works properly, but I need it to display a specific value that is stored in a variable based on the user's selection. For example, let's suppose we ...

Does React trigger a re-render when setState is called even if the state value remains unchanged?

Imagine I create a React component with an initial state of {random: 1}. Now, if I were to execute the following code: this.setState({random: 1}) Would this action result in triggering a re-render of the component? Furthermore, is there a method to avoid ...

Unauthorized access for POST request in WooCommerce API: 401 error

Let's start by examining the complete code to better understand the issue at hand. Here is the WooCommerce API authentication using the consumer key and secret from the file checkout.ts: this.WooCommerce = WC({ url:"http://localhost/ ...

In-line Vertical Ticker Display

I attempted to use vTicker, but I found that it does not function properly when used as an inline element. <h1> <div id="vticker"><ul>...</ul></div> Some other headline text </h1> My goal is to have the vertica ...

Concealing Panels within EasyUi Tab Navigation

There is an easyui Tab that consists of 4 tabs. After receiving a response from the server, it may be necessary to hide or show some of the tabs. I attempted to remove the tabs initially and add them back later. However, the issue arises when the tabs are ...

React does not automatically re-render components created with the built-in function

I'm facing some confusion with the behavior in my code: I've created a component that should function as a menu using MaterialUI. The idea is that when a button in the menu is clicked, it becomes "active" and visually reflects this change by set ...

A comprehensive guide on properly obtaining user details in React with Redux and OIDC

Although I've dabbled in OIDC before, I wouldn't consider myself an expert. Currently, I'm trying to integrate OIDC into a react app using oidc-client-js and redux-oidc libraries, following the redux-oidc-example as a guide. Encountering th ...

Trouble with executing asynchronous AJAX request using when() and then() functions

Here is the code that I am currently using: function accessControl(userId) { return $.ajax({ url: "userwidgets", type: "get", dataType: 'json', data: { userid: userId } }); }; var user ...

How come the likes are not being refreshed when the button is clicked in my mongoose schema?

I am currently working on an express app using nodejs and mongoose. My main goal is to implement a JavaScript function that can update the number of likes on a post in a database directly from my ejs file. However, I have encountered troubles with this tas ...

The intersection of conditional types and the combination of string literals with class unions

Encountered an unusual behavior in the types system when trying to type the as prop from emotion. import React, { Component, FC, PropsWithChildren } from "react"; // Defining possible types for `as` prop type AsType = | keyof JSX.IntrinsicElements | ...

Having trouble accessing req.user on my Node.js server using Auth0 and Angular

Currently, I am utilizing auth0 for my admin panel's login system and it is functioning smoothly. However, I have encountered an issue in node where 'req.user' is returning as undefined for some unknown reason. This setup is fairly basic; I ...

Encounter a scope.router.go error when using Vue.js with System.js

During my testing of a Vue.js-System.js app before transitioning to webpack2, I encountered an issue with the routing pattern. In the OPA Memberships component, when clicking on a link, I aim to request a Registration page from the router. To achieve this ...

The backend function of an aspx page is failing to execute when triggered by a $.ajax request

Currently, I am facing an issue with a web form IndexPage.aspx. The script in this front-end code makes an ajax call to a code-behind function. Take a look at the code snippet below: $.ajax({ type: "POST", url: &apos ...

What is the best way to retrieve a URL from a function in an HTML/JavaScript environment?

Recently, I delved into learning HTML and JavaScript. One of the challenges I encountered is figuring out how to access a URL or download a file from a function written in JavaScript that was imported from another file using: <script src="jscriptSheet. ...

Is it not possible to apply the .includes method on a string within a v-if directive while utilizing a computed property?

Problem I am facing an issue while trying to determine if a string contains a substring within the Vues v-if directive. The error message I receive is: TypeError: $options.language.includes is not a function The technologies I am using are Vue, Vuex, and ...

"Experiencing sluggish performance with VSCode while using TypeScript and Styled Components

My experience with vscode's type-checking is frustratingly slow, especially when I am using styled components. I have tried searching for a solution multiple times, but have only come across similar issues on GitHub. I attempted to read and understa ...

In Javascript, have an audio clip play every 30 seconds

My webpage has a unique feature - it automatically plays a short audio clip as soon as the page loads. This functionality is achieved using a special audio player called nifty player. Here is the code snippet for embedding the player: <object classid= ...

Is utilizing a standardized logging system considered a best practice for a node and express application?

I am currently working on a node.js application that consists of several dozen modules and uses bunyan for logging with JSON output and multiple configurable streams. I have been searching for good examples on how to implement a logger instance across all ...