All answers that seem to tackle a similar issue relate to Vue 2 or simply don't give the expected results. Even what's described in the docs doesn't work, because if I log the value of the ref I just see an empty object.
I have a template like so:
<template>
<post-content v-for="post in posts" :post-data="post" :key="post.id" ref="lastPost" />
</template>
The content of the PostContent component is unimportant, imagine it as a div displaying whatever's into post.content
.
In the script I fetch posts
from an API, and I'd like to have the last loaded post in the reference lastPost
, so that I can access its HTMLElement
(I need it for stuff, but here I just try to log it).
// This uses composition API with <script setup> syntax
const posts = ref<{id: number, content: string}[]>([])
getPostsFromApi()
.then((thePost: {id: number, content: string}) => posts.value.push(thePost))
const lastPost = ref<typeof PostContent>()
watch(lastPost, () => nextTick(() => console.log(lastPost.value)), {flush: "post"})
However, this results in the log being a simple empty object {}
.
- I need to be able to access the HTMLElement itself once it has loaded.
- I need to watch the reference for changes, so that I can get the element every time I add posts.
- I only need the last post at any given time, I don't care about previous ones. I will only add posts one at a time. This is why I'm not using an array as a ref.
Why is an empty object being logged instead of what, according to the docs, should be expected? What am I doing wrong?
CodePudding user response:
Unlike SFCs with a regular <script>
block, <script setup>
components are closed by default -- i.e. variables inside the <script setup>
scope are not exposed to the parent unless explicitly exposed via defineExpose()
. An empty object in the logged template ref implies you haven't exposed any properties.
To expose the root element of PostContent
, use a template ref in the component, and expose the ref
(e.g., named "$el
") with defineExpose()
:
// PostContent.vue
<script setup lang="ts">
const $el = ref()
defineExpose({ $el })
</script>
<template>
<div ref="$el">...</div>
</template>
As an aside, watch()
with { flush: 'post' }
can be simplified to watchPostEffect()
:
watch(lastPost, () => nextTick(() => console.log(lastPost.value.$el)), {flush: "post"})
// OR more simply:
watchPostEffect(() => console.log(lastPost.value.$el))