Skip to content

Determine if slot is empty #3056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
plehnen opened this issue Jan 19, 2021 · 8 comments
Closed

Determine if slot is empty #3056

plehnen opened this issue Jan 19, 2021 · 8 comments

Comments

@plehnen
Copy link

plehnen commented Jan 19, 2021

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)

@posva
Copy link
Member

posva commented Jan 19, 2021

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()

@posva posva closed this as completed Jan 19, 2021
@plehnen
Copy link
Author

plehnen commented Jan 19, 2021

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
Copy link
Author

plehnen commented Jan 19, 2021

@matt-snider
Copy link

@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?

@plehnen
Copy link
Author

plehnen commented Feb 24, 2021

@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 #3257

@posva
Copy link
Member

posva commented Feb 25, 2021

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)

@matt-snider
Copy link

@posva Thank you for your response! That is helpful 👍

@plehnen
Copy link
Author

plehnen commented Feb 26, 2021

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() !== ''));
};

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants