core: Request to add an official way to check if slot is empty
What problem does this feature solve?
There are some times in component composition when we need the ability to determine whether a slot has been passed down by the user of the component with some content. In Vue 2 we could check this easily:
<div v-if="this.$slots.mySlot">
<p>Some unrelated content to the slot or extra markup</p>
<slot name="mySlot" />
</div>
With the addition of fragments and the changes to the slot system in Vue 3, this simple truthy/falsy check is no longer possible since a slot function will return a truthy object when containing empty text, comments, fragments, etc.
Vue internals are checking this here: https://github.com/vuejs/vue-next/blob/8610e1c9e23a4316f76fb35eebbab4ad48566fbf/packages/runtime-core/src/helpers/renderSlot.ts#L59
However, there is no official way to do this on the user end that I have found that works 100% of the time without having to tap into undocumented internal API.
Some folks are suggesting checking against the symbol in vnode.type, which works in theory however it seems that when compiled these symbols usually evaluate to undefined. It also creates a very hard-to-maintain series of if checks to try to determine the type of vnode that is being received, along with a combination of checks on its `children.
There was an attempt to request this early in January, but sadly it was closed and dismissed with some answers that don’t seem to fully solve the problem. https://github.com/vuejs/vue-next/issues/3056
What does the proposed API look like?
The dream would be to expose some sort of slotIsEmpty (naming?) function through the vue package, similarly to how we are getting isRef, etc. that would simplify solving this particular problem.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 18
- Comments: 24 (3 by maintainers)
My version of helper:
Vote for official way.
There seems to still be a lot of back and forth and people struggling with this, so I’m sharing the composable I use at work for whoever needs: https://gist.github.com/marina-mosti/99b08783b161fa4ba21ebd2ec664fa14#file-useemptyslotcheck-js It builds up on some great ideas posted here, and can probably be improved in many ways but at least is already copy-pasteable
TS version of @lehni’s helpers in case it’s useful for anyone else:
Edited to make
asArraymore flexible.I think this at least deserves mention in the migration guide. It is a breaking change without any documentation.
While @andrey-hohlov helper is nice, but I’d rather not handle this on the application level – it’s not easy to understand and maintain this piece of code.
Couldn’t we reuse the same definition of “empty VNode” from Vue 2? Here’s a fork of the previously mentioned sandbox, but using Vue 2 – https://codesandbox.io/s/vue-2-empty-slot-handling-pb50zt?file=/src/HelloWorld.vue.
EDIT:
seems to exactly match Vue 2 behaviour When slot content is:
<div v-if="false"></div>- does not render<div v-if="true"></div>- renders<Component />- renders{{ undefined || null || false || "" }}- rendersv-if="$slots.nonexsisting"/v-if="hasSlotContent($slots.nonexisting)"- does not throw an error<slot />- renders only if it has content passed from parentEDIT I think the function above might still be incorrect in some cases (e.g. when using
<Suspense>or<Transition>), use with cautionInstead of being slot specific, it could also be vnode specific, like
isVNodeEmpty(arrayOrSingleVNode):One issue is using this in a template would be much verbose in order to reuse the vnodes. So maybe there is an alternative way of doing this that could unify how to do it.
Another issue is that what is empty could be subjective, due to trailing spaces and how they are handled, see https://github.com/vuejs/vue-next/issues/3056#issuecomment-786560172 considering trimming whitespace while in other scenarios they shouldn’t be trimmed to consider a slot empty.
Here is a version that removes comments, it can be adapted:
BTW I find
asArray()generally useful and would allow it any argument types, nut just VNodesHere my version of
hasSlotContent(), along with other useful helpers. I found it a bit easier to understand what’s actually happening when structured this way:Please check this RFC out.
The helper from @andrey-hohlov works for me!
However I’d feel better if this was built-in officially so that I don’t need to worry about that helper stop working after some future update of vue.
I think many people will face this issue, in particular regarding that many posts on stackoverflow mention the simple condition approach
if ($slots.default) { ... }which doesn’t work anymore.I don’t think it makes sense to add this API if we cannot reach a consensus on what
isVNodeEmpty()means (ref the comment I linked).There is nothing internal about that function, it’s safe to use