Lets say I have a Component.vue
<template>
<div>
<div class="items">
<slot name="items"></slot>
</div>
<div class="controller"></div>
<slot name="controller></slot>
</div>
</div>
</template>
<script lang="ts" setup>
// HERE I need to access the exposed properties of every component passed to the 'items' slot from the parent
</script>
and a ComponentChild.vue
<template>
<div ref="el" class="item">
<slot></slot>
</div>
</template>
<script lang="ts" setup>
const el = ref<HTMLDivElement>()
const itemEl = ref<HTMLElement>()
onMounted(() => {
itemEl.value = el.value!.children[0] as HTMLElement
})
defineExpose({
itemElement: itemEl,
})
</script>
And here is the usage:
<template>
<div>
<Component>
<template #items>
<ComponentChild v-for="item in items" :key="item.id">
<div>The element I'm trying to access from Component exposed as itemElement in ComponentChild</div>
</ComponentChild>
</template>
<template #controller>
<div>...</div>
</template>
</Component>
</div>
</template>
<script lang="ts" setup>
import Component from "@/components/Component.vue"
import ComponentChild from "@/components/ComponentChild.vue"
</script>
The question is how to access that itemElement
for each ComponentChild from Component.vue?
Now I know this may not be the best way to access the element ref in this case but what if I want to expose something else (not an element ref) and want access it from Component. So I don't need alternatives.
This is done by using render function, this allows to modify the contents of a slot and includes adding the refs. The support for built-in directives (v-for
, etc) needs to be explicitly provided as the structure of slot vnodes will be different:
setup(props, { slots }) {
const childInstances = shallowRef([]);
let children = slots.items?.();
if (children?.length === 1 && children[0].type === Fragment) {
children = children[0].children;
}
return () => {
return h(
'div',
{ class: "items" },
children?.map((vnode, index) =>
h(vnode, { ref: instance => childInstances.value[index] = instance })
)
);
};
}
As long as ComponentChild
exposes nothing but root element, it may be unnecessary to use defineExpose
; the element will be available as instance.$el
.