1

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:

  • console.log(projectElementRefs.value) - Proxy Array with 10 items.
  • console.log(projectElementRefs.value[0]) - Proxy object, but not the DOM element.
  • console.log(projectElementRefs.value[0].value) - undefined.
  • console.log(listItemRef.value) - Proxy Array of 3 items.
  • console.log(listItemRef.value[0]) - The actual <li> element.

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.

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.