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

huangapple go评论78阅读模式
英文:

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

问题

我正在使用Vue 3和Nuxt 3,使用Composition API和<script setup>语法。我遇到了一个问题,当我在v-for中使用模板引用时,无法访问自定义组件的DOM元素。与之相反,对于基本的<li>元素,这种方法按预期工作。我的问题主要集中在模板引用与v-for在这种情况下结合时的奇怪行为上。

这是我代码的简化版本:

模板:

<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 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);             // 长度为10的代理数组
  console.log(projectElementRefs.value[0]);         // 代理对象,而不是实际的值
  console.log(projectElementRefs.value[0].value);   // undefined
  console.log(listItemRef.value);                   // 长度为3的代理数组
  console.log(listItemRef.value[0]);                // <li>元素
}

为了清楚起见,此点击事件在组件挂载后触发。console.log语句返回以下结果:

  • console.log(projectElementRefs.value) - 长度为10的代理数组。
  • console.log(projectElementRefs.value[0]) - 代理对象,而不是DOM元素。
  • console.log(projectElementRefs.value[0].value) - undefined
  • console.log(listItemRef.value) - 长度为3的代理数组。
  • console.log(listItemRef.value[0]) - 实际的<li>元素。

有人遇到过类似的问题吗?在文档中,它说在挂载后应该用元素填充。

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

如果有人知道如何从我的模板引用数组中的v-for访问元素,我将非常感激。

英文:

I'm working with Vue 3 and Nuxt 3 using the Composition API and the &lt;script setup&gt; 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 &lt;li&gt; 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:

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

Script (using &lt;script setup&gt;):



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

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

const handleClick = (project, index) =&gt; {
  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]);                // &lt;li&gt; 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 &lt;li&gt; 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

得分: 1

从Vue的文档中可以看到:

> 一个例外是使用<script setup>的组件默认是私有的:父组件引用使用<script setup>的子组件时,除非子组件选择使用defineExpose宏公开公共接口,否则无法访问任何内容。

我假设这是您的ProjectCard组件中的基本代码:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

&lt;script lang=&quot;ts&quot; setup&gt;
const props = defineProps&lt;{
    project: string
}&gt;()

const projectRef = ref&lt;HTMLElement | null&gt;(null)

&#128071;&#127997; // Don&#39;t forget this.
defineExpose({
    projectRef
})

&lt;/script&gt;
&lt;template&gt;
    &lt;div ref=&quot;projectRef&quot;&gt;
        {{ project }}
    &lt;/div&gt;
&lt;/template&gt;
&lt;style scoped lang=&quot;css&quot;&gt;&lt;/style&gt;

<!-- end snippet -->

现在,在您的父组件或页面中:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

&lt;script setup&gt;
const { data: projects } = await useFetch( &#39;/api/projects&#39; )


const projectElementRef = ref( null )

function onHandleProject( index ) {
    console.log( &#39;Clicked project &#39;, projectElementRef.value[ index ].$refs.projectRef.innerText )
}
&lt;/script&gt;
&lt;template&gt;
    &lt;div&gt;
        &lt;ProjectCard
            v-for=&quot;(project, index) in projects&quot;
            :key=&quot;project&quot;
            :project=&quot;project&quot;
            ref=&quot;projectElementRef&quot;
            @click=&quot;onHandleProject(index)&quot;
        /&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;style scoped lang=&quot;css&quot;&gt;&lt;/style&gt;

<!-- end snippet -->

在测试时,预期的输出将如下所示。
Unable to access the element when using template refs with v-for in Vue3/Nuxt3 on a custom component

如果您想要访问HTML元素

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

<!-- end snippet -->

预期的输出将是这样的。
Unable to access the element when using template refs with v-for in Vue3/Nuxt3 on a custom component

项目API示例。

~/server/api/projects.get.ts

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

<!-- end snippet -->

希望能对您有所帮助。

英文:

From the Vue documentation

> An exception here is that components using <script setup> are private
> by default: a parent component referencing a child component using
> <script setup> 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

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

&lt;script lang=&quot;ts&quot; setup&gt;
const props = defineProps&lt;{
    project: string
}&gt;()

const projectRef = ref&lt;HTMLElement | null&gt;(null)

&#128071;&#127997; // Don&#39;t forget this.
defineExpose({
    projectRef
})

&lt;/script&gt;
&lt;template&gt;
    &lt;div ref=&quot;projectRef&quot;&gt;
        {{ project }}
    &lt;/div&gt;
&lt;/template&gt;
&lt;style scoped lang=&quot;css&quot;&gt;&lt;/style&gt;

<!-- end snippet -->

Now, in your parent component or page.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

&lt;script setup&gt;
const { data: projects } = await useFetch( &#39;/api/projects&#39; )


const projectElementRef = ref( null )

function onHandleProject( index ) {
    console.log( &#39;Clicked project &#39;, projectElementRef.value[ index ].$refs.projectRef.innerText )
}
&lt;/script&gt;
&lt;template&gt;
    &lt;div&gt;
        &lt;ProjectCard
            v-for=&quot;(project, index) in projects&quot;
            :key=&quot;project&quot;
            :project=&quot;project&quot;
            ref=&quot;projectElementRef&quot;
            @click=&quot;onHandleProject(index)&quot;
        /&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;style scoped lang=&quot;css&quot;&gt;&lt;/style&gt;

<!-- end snippet -->

While testing, the expected output will be like this.
Unable to access the element when using template refs with v-for in Vue3/Nuxt3 on a custom component

If you want to access the HTML elements

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

<!-- end snippet -->

The expected output will be this.
Unable to access the element when using template refs with v-for in Vue3/Nuxt3 on a custom component

The projects API example.

~/server/api/projects.get.ts

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

<!-- end snippet -->

Hope that helps.

huangapple
  • 本文由 发表于 2023年8月8日 20:03:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76859379.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定