英文:
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 <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
得分: 1
从Vue的文档中可以看到:
> 一个例外是使用<script setup>的组件默认是私有的:父组件引用使用<script setup>的子组件时,除非子组件选择使用defineExpose宏公开公共接口,否则无法访问任何内容。
我假设这是您的ProjectCard
组件中的基本代码:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
<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>
<!-- end snippet -->
现在,在您的父组件或页面中:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
<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>
<!-- end snippet -->
如果您想要访问HTML元素
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function onHandleProject( index ) {
console.log( 'Clicked project ', projectElementRef.value[ index ].$refs.projectRef )
}
<!-- end snippet -->
项目API示例。
~/server/api/projects.get.ts
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
export default defineEventHandler((event) => {
const projects = []
for (let x = 0; x < 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 -->
<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>
<!-- end snippet -->
Now, in your parent component or page.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
<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>
<!-- end snippet -->
While testing, the expected output will be like this.
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( 'Clicked project ', projectElementRef.value[ index ].$refs.projectRef )
}
<!-- end snippet -->
The expected output will be this.
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) => {
const projects = []
for (let x = 0; x < 10; x++) {
projects.push(`Project num ${x}`)
}
return projects
})
<!-- end snippet -->
Hope that helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论