anywhereseason
anywhereseason

Reputation: 233

Unable to access the element when using template refs with v-for in Vue3/Nuxt3 on a custom component

I'm working with Vue 3 and Nuxt 3 using the Composition API and the <script setup> syntax. I've encountered an issue where I'm unable to access the DOM elements of my custom component when using template refs with v-for. Contrastingly, the approach works as expected with a basic <li> element. The focus of my issue revolves around the peculiar behaviour of template refs when combined with v-for in this context.

Here's a reduced version of my code:

Template:

<template>
  <main>
    <section v-if="projectData && projectData.projects" ref="projectSection">
      <li v-for="item in listItems" ref="listItemRef">
        {{ item }}
      </li>
      <div ref="sectionContainer">
        <ProjectCard
          v-for="(project, index) in projectData.projects"
          ref="projectElementRefs"
          :key="index"
          :project="project"
          @click="handleClick(project, index)"
        />
      </div>
    </section>
  </main>
</template>

Script (using <script setup>):



const projectData = await fetchData({
  query: homeQuery,
});

const listItemRef = ref(null);
const listItems = ref(['item1', 'item2', 'item3']);
const projectElementRefs = ref([]);

const handleClick = (project, index) => {
  console.log(projectElementRefs.value);             // Proxy Array of 10 in length
  console.log(projectElementRefs.value[0]);         // Proxy object, not the value itself
  console.log(projectElementRefs.value[0].value);   // undefined
  console.log(listItemRef.value);                   // Proxy Array of 3 in length
  console.log(listItemRef.value[0]);                // <li> element
}

For clarity, this click event triggers after the component has mounted. The console.log statements return the following:

Has anyone encountered a similar challenge? In the docs, it says it should be populated with the elements after mounting.

https://vuejs.org/guide/essentials/template-refs.html

I would be so grateful if anyone knows how to access the element from my template ref array from v-for.

Upvotes: 1

Views: 3202

Answers (1)

Reagan
Reagan

Reputation: 2385

From the Vue documentation

An exception here is that components using are private by default: a parent component referencing a child component using

won't be able to access anything unless the child component chooses to expose a public interface using the defineExpose macro:

I'll assume that this are the basic codes inside your ProjectCard component

<script lang="ts" setup>
const props = defineProps<{
    project: string
}>()

const projectRef = ref<HTMLElement | null>(null)

šŸ‘‡šŸ½ // Don't forget this.
defineExpose({
    projectRef
})

</script>
<template>
    <div ref="projectRef">
        {{ project }}
    </div>
</template>
<style scoped lang="css"></style>

Now, in your parent component or page.

<script setup>
const { data: projects } = await useFetch( '/api/projects' )


const projectElementRef = ref( null )

function onHandleProject( index ) {
    console.log( 'Clicked project ', projectElementRef.value[ index ].$refs.projectRef.innerText )
}
</script>
<template>
    <div>
        <ProjectCard
            v-for="(project, index) in projects"
            :key="project"
            :project="project"
            ref="projectElementRef"
            @click="onHandleProject(index)"
        />
    </div>
</template>

<style scoped lang="css"></style>

While testing, the expected output will be like this. enter image description here

If you want to access the HTML elements

function onHandleProject( index ) {
    console.log( 'Clicked project ', projectElementRef.value[ index ].$refs.projectRef )
}

The expected output will be this. enter image description here

The projects API example.

~/server/api/projects.get.ts

export default defineEventHandler((event) => {
    const projects = []
    for (let x = 0; x < 10; x++) {
        projects.push(`Project num ${x}`)
    }
    return projects
})

Hope that helps.

Upvotes: 1

Related Questions