Determine if slot is empty
What problem does this feature solve?
In vue2 it was possible to test the existence of $scopedSlots.default to check if the slot has content. Even if the slot content was depending on conditions. (e.g. <b v-if="false">content</b>
)
For example in vue2 this was possible:
<template functional> <!-- for VDescriptionList component -->
<dl v-if="$scopedSlots.default">
<slot />
</dl>
</template>
...
<VDescriptionList>
<VEntry :data="data" field="a" optional />
<VEntry :data="data" field="b" optional />
</VDescriptionList>
If the data did neither contain property a
or b
none of the VEntry components are rendered, therefore no VDescriptionList needed to be rendered (which would affect the design and the semantic DOM interpreted by screenreaders).
In vue3 this.$slots.default()
(or ctx.slots.default()
for functional components) of a content like <b v-if="false">content</b>
is not undefined
(like it was in vue2) but instead is an array filled with one element.
I use this feature in several parts of my project to detect if the rendered slot content is actually containing anything. Currently I can't see any way to migrate my components from 2 to 3 without loosing this feature.
What does the proposed API look like?
Ideally there would be a way to read if a slot does contain anything. I understand that it is required to execute the slot, because the fact if it's actually filled could be based on slot scoped parameters. But in case there are no parameters, it seems like a huge waste to execute the code twice (once for detecting the value to see if it contains anything, and a second time to actually render it)
Hi, thanks for your interest but Github issues are for bug reports and feature requests only. You can ask questions on the forum, the Discord server or StackOverflow.
You have to check and call the function and look at the returned value with slots.default()
It wasn't a question. I know that I have to call the slots.default() function. The problem is that in vue2 the value is "undefined" as expected when the body is empty, but in vue3 the value is filled, even if the body is just a <b v-if="false">not visible at all</b>
code. And this makes it impossible to migrate such components from vue2 to vue3.
@plehnen Thank you for posting your solution, but I'm wondering if this is actually a safe solution that can be trusted since it seems to use some internal undocumented APIs. @posva Would you be able to comment on that snippet and whether this is a legitimate solution?
@matt-snider FYI: There are cases where vue3 will output a warning that slots are accessed outside the render function. (If you try this approach with the new composition API) More details here https://github.com/vuejs/vue-next/issues/3257
I think this should do:
function isEmptySlot(slot, data)
return slot(data).filter(vnode => vnode.type !== Comment).length < 1
}
isEmptySlot(slots.default, { someData: ... })
isEmptySlot(slots.default)
If you want to be sure that the slot is considered empty even if the content is something like {{ emptyVariable }}
you have to check for empty text too, e.g.:
export const stripEmpty = (slot, data) => {
return slot(data).filter(vnode => vnode.type !== Comment && (vnode.type !== Text || vnode.children.trim() !== ''));
};