Skip to content
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

Can not clear slot content with vue@2.5.3 #7064

Closed
yibuyisheng opened this issue Nov 15, 2017 · 23 comments
Closed

Can not clear slot content with vue@2.5.3 #7064

yibuyisheng opened this issue Nov 15, 2017 · 23 comments

Comments

@yibuyisheng
Copy link

Version

2.5.3

Reproduction link

https://jsfiddle.net/yibuyisheng/s5xoxmv1/6/

Steps to reproduce

Create a Base Component with a slot, and pass in the slot with template tag with empty content.

What is expected?

The empty template slot should clear the content of Base Component's corresponding slot.

What is actually happening?

Did not clear the content.


However, the vue@2.4.4 do the right thing: https://jsfiddle.net/yibuyisheng/s5xoxmv1/7/

@yyx990803
Copy link
Member

IMO 2.4.4 is behaving incorrectly and 2.5.3 fixes the behavior. By design, fallback content appears when a slot receives nothing or all whitespace. "Clearing a slot's fallback content with an empty <template> slot" sounds like a hack to me.

However if you still want to do that, you can use the zero-width space (&#8203;) as slot content.

@Justineo
Copy link
Member

Using zero-width space to express “clearing the fallback content of a slot” sounds like a hack to me either...is there a better way for us to solve this with more explicit semantics?

@fnlctrl
Copy link
Member

fnlctrl commented Nov 16, 2017 via email

@Justineo
Copy link
Member

@fnlctrl

Consider you have a complex component with multiple slots, you wouldn't want to expose that kind of prop to each of them. Using slots means we prefer a declarative way to define content, otherwise we can just ignore the slot syntax and pass render functions through props.

@yibuyisheng
Copy link
Author

@fnlctrl
Maybe we should add useDefaultSlot to the template attribute, and provide a global config to indicate the default override behavior.

@fnlctrl
Copy link
Member

fnlctrl commented Nov 16, 2017

@Justineo For complex component with multiple slots, even if we provided an official api to suppress default content, users would still have to declare which slots to be suppressed.
e.g.

<foo :defaults="{slotA: false, slotB: false}"/>

And I don't think it's any better than declaring props in userland.

The best way I can think of is to simply use an empty <div> or <p> or whatever tag like

<foo>
  <p slot="a"/>
  <p slot="b"/>
</foo>

It's simple and declarative.

@Justineo
Copy link
Member

Actually I'm not seeing why the behavior back to 2.4.4 is undesired. By declaring <template slot="a">...</template>, users are already explicitly overriding the fallback content, otherwise they wouldn't declare them at all. If they want to conditionally override the fallback content, they can apply v-if like <template slot="a" v-if="override">.

And I don't think it's any better than declaring props in userland.

If overriding slot fallback content is a common scenario, a built-in syntax will definitely help users to build apps with lower overhead on designing this kind of API. And this helps Vue users to understand what is the underlying logic of the template across different Vue projects.

@Jinjiang
Copy link
Member

Jinjiang commented Nov 16, 2017

I think the "fallback content" is different with "default content", if we have defined fallback content, any behavior to make the content "empty" should be considered as a hack at first. From this point I agree with @yyx990803 . So here hack with a zero-width space is just good to me. Or you can define a "conditional" fallback content as @fnlctrl provided to set the content empty. Or you never define a fallback content as the default content.
Thanks.

@adi518
Copy link

adi518 commented Apr 10, 2018

For a more consistent solution, I'd create an empty functional (stateless for better performance) component with a span element, which is completely layout-less, because it's an inline element. Using a p tag can introduce user-agent margin:

<template functional>
  <span></span>
</template>

<script>
export default { name: 'VEmpty' }
</script>

// Usage
<template>
  <v-foo>
    <v-empty></v-empty>
  </v-foo>
</template>

2.4.4 did seem more natural to me. I recently upgraded to 2.5.x and ended up here. :)

@Justineo
Copy link
Member

Justineo commented Apr 11, 2018

@adi518

  1. This is not “completely layout-less” because even empty DOM elements will match CSS selectors like :first-child, :last-child, etc.

  2. A bare <span> element is more performant than a functional component IMO.

  3. <span> element is not always a valid child for a given parent element. Eg. <ul>, <tr>, etc.

@adi518
Copy link

adi518 commented Apr 11, 2018

A bare <span> element is not consistent. An encapsulated solution is better IMO. If <span> creates a problem, use <i>. That should be valid pretty much everywhere. With component-based development, I suggest refraining from pseudo selectors/elements as their implicitness is a con. In the older days it used to save you some markup, now it is no longer an issue since components reduce the average amount of code you have to write anyway. Instead of pseudo selectors, use classes, which are explicit and give you better control.

@Justineo
Copy link
Member

<i> is basically <span> with special semantics, it shares the same categories with <span>. Neither of them can be used inside <ul>, <tr> and a lot of other elements.

Instead of pseudo selectors, use classes, which are explicit and give you better control.

Yes you remind me of the old days that I have to support IE6...But what I want is a clean solution that does not prevent developers from leveraging modern technologies such as pseudo classes.

@adi518
Copy link

adi518 commented Apr 11, 2018

Well, it's opinionated. With a year of developing a library, I didn't find much use for them or sense their absence. Not using <i> in those tags doesn't sound probable. See here.

@plehnen
Copy link

plehnen commented May 11, 2018

@yyx990803
Sad that this is closed.
I also would like to be able to clear the default slot content by an empty slot.

e.g. if you have a default "separator"-slot. Sometimes the iterated components may not have any html-tags in between because they break the float css styles. Also it's not clean to just use "empty" tags if it would be more logical to use an empty template.
(and using v-if's is quite verbose)

So if an exists, it should override the default value.
This behaviour is logical. Why else should one define the template, if there is a default content. If I want the default content to be shown, I simply remove the empty template.

@adi518
Copy link

adi518 commented Jul 20, 2018

@Justineo Would have been nice if you posted your fix. 👍

@alexshink
Copy link

alexshink commented Oct 30, 2019

So far, the best I've come up with is to use an empty separate component with a template:
<i v-if="false"></i>, which allows you to bypass the rendering of unnecessary empty tags.

@WORMSS
Copy link
Contributor

WORMSS commented Nov 20, 2019

IMO 2.4.4 is behaving incorrectly and 2.5.3 fixes the behavior. By design, fallback content appears when a slot receives nothing or all whitespace. "Clearing a slot's fallback content with an empty <template> slot" sounds like a hack to me.

However if you still want to do that, you can use the zero-width space (&#8203;) as slot content.

@yyx990803
Zero-width space seems to act incorrectly. it adds the equivalent of new lines between two divs.
if you add an empty span, the layout is correct, but creates problems with css.

It would be far easier to just pass zero content. It seems the most logical to the most amount of people, and adds zero hacks. The bug seems to be the fix from 2.4.4 to 2.5.3 Blank content is exactly what I want within the slot.

@Justineo
Copy link
Member

@WORMSS Maybe you'll be interested in this: https://github.com/Justineo/vue-void

@avblink
Copy link

avblink commented Dec 17, 2020

Here is another solution:

<template v-slot:header><div style="display:none"></div></template>

@sethidden
Copy link

sethidden commented Jan 10, 2021

It's not much better but you can also do

<template #header><div v-show="false"/></template>

The above will break :first-child CSS selectors if you use them in your slot

#header === v-slot:header obviously


Justineo's solution above is really cool. You may be scared since it's a separate npm package - but it's as simple as:

export default {
  name: 'App',
  components: {
    HelloWorld,
    VVoid: {
      name: 'v-void',
      render(h) {
        return h();
      }
    }
  },
}

You can also register it globally for convenience.

@WORMSS
Copy link
Contributor

WORMSS commented Jan 10, 2021

<div v-show="false"/>

Wont that still add an unwanted div. Just because it's display: none can still potentially break CSS

@sethidden
Copy link

Yes it'll break :first-child selectors (the thing v-void's readme warns about). I edited my comment. The only benefit is that it uses simple template syntax

@iv660
Copy link

iv660 commented Mar 2, 2022

Although it's a hack, too, the following code seems to do a trick without introducing any extra HTML:

<template>
  {{null}}
</template>

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

No branches or pull requests