qwik icon indicating copy to clipboard operation
qwik copied to clipboard

Slot fallback not working

Open O-Hooman opened this issue 3 years ago • 10 comments

Qwik Version

qwik: 0.0.101, qwik-city: 0.0.101

Operating System (or Browser)

MacOS Monterey 12.3.1 / Chrome 104.0.5112.79

Node Version (if applicable)

14.19.1

Which component is affected?

Qwik Runtime

Expected Behaviour

Fallback slot showing

Actual Behaviour

Fallback slot not showing

Additional Information

on typescript error: Type '{ children: string; name: string; }' is not assignable to type 'IntrinsicAttributes & { name?: string; }'. Property 'children' does not exist on type 'IntrinsicAttributes & { name?: string; }'.(2322)

also tried it on the playground and it's not working

O-Hooman avatar Aug 22 '22 04:08 O-Hooman

Since 0.0.100 we removed the extra dom node for host components and slots. This made it very complicated to support fallback content, however now it's easier to target and implement yourself fallback content with css. We might add fallback later.

manucorporat avatar Aug 22 '22 09:08 manucorporat

@manucorporat ah I see, thank you for the info

O-Hooman avatar Aug 25 '22 02:08 O-Hooman

Perhaps the tutorial should be updated to reflect this.

csdswarm avatar Oct 06 '22 14:10 csdswarm

@manucorporat what is the plan for fallback? The only real alternative is to pass content as a prop and then render that, but that's quite ugly

Here's the tutorial solution if you do it like that:


export const Card = component$<{renderBody?, renderTitle?}>(({renderBody, renderTitle}) => {
  useStyles$(CSS);
  const title = renderTitle ? renderTitle() || "fallbackTitle" : <Slot name="title"/>
  const body = renderBody ? renderBody() || <div>fallback body</div> : <Slot/>
  return (
    <div class="card">
      <div class="title">
        {title}
      </div>
      <div class="body">
        {body}
      </div>
    </div>
  );
});

export const App = component$(() => {
  return (
    <>
      <Card>
        <span q:slot="title">Qwik</span>
        <span>Qwik is a resumable framework for building instant web apps.</span>
      </Card>
      <Card renderTitle={()=>"Partytown"} renderBody={()=>{}}>
      </Card>
      <Card>
        <span>
          Builder.io allows you to visually build on your tech stack Empower your entire team to
          visually create and optimize high-speed experiences on your sites and apps. Provide
          whole-team autonomy with a platform that is developer approved.
        </span>
      </Card>
    </>
  );
});

or is there a nicer way?

wmertens avatar Oct 07 '22 10:10 wmertens

Actually, those render functions can't rerender when they depend on changing data. I'm not even sure how to do that correctly.

wmertens avatar Oct 07 '22 11:10 wmertens

So, the way you can do it is something like this (note the additional css div and the sibling selector in the CSS):

import { component$, Slot, useStyles$ } from '@builder.io/qwik';

export const Card = component$(() => {
  useStyles$(CSS);
  return (
    <div class="card">
      <div class="title">
        <Slot name="title"></Slot>
      </div>
      <div class="body">
        <Slot></Slot>
      </div>
      <div class="body fallback">Fallback content</div>
    </div>
  );
});

export const App = component$(() => {
  return (
    <>
      <Card>
        <span q:slot="title">Qwik</span>
        <span>Qwik is a resumable framework for building instant web apps.</span>
      </Card>
      <Card>
        <span q:slot="title">Partytown</span>
      </Card>
      <Card>
        <span>
          Builder.io allows you to visually build on your tech stack Empower your entire team to
          visually create and optimize high-speed experiences on your sites and apps. Provide
          whole-team autonomy with a platform that is developer approved.
        </span>
      </Card>
    </>
  );
});

export const CSS = `
.card {
  border-radius: 5px;
  vertical-align: top;
  display: inline-block;
  border: 1px solid grey;
  width: 200px;
  margin: .5em;
}

.title {
  background-color: lightgray;
  padding: 0.5em;
  border-bottom: 1px solid black;
}

q\\:fallback {
  color: gray;
}

.body {
  padding: 0.5em;
}
.body:empty {
  display: none;
}

.body.fallback {
  display: none;
}

.body:empty + .body.fallback {
  display: block;
}
`;

csdswarm avatar Oct 07 '22 12:10 csdswarm

That's great thanks! I was thinking we had to use content but this is way easier.

It's still not optimal though, if you have content and it will never change, then the html contains the fallbacks for all those cards for no reason.

EDIT: Actually, doing it with CSS is straightforward, so if it's simply text, the solution is this:

.title:empty::before {
  content: 'Fallback title'
}

wmertens avatar Oct 07 '22 12:10 wmertens

Yeah, would be helpful if there were a way to check if the "slot" were empty using script or something. I'm a bit too busy to look into this in depth myself, but if I come across some time I may see if I can figure out a better way and submit

csdswarm avatar Oct 07 '22 12:10 csdswarm

@csdswarm I think you're already in a good position to update the tutorial so that new users aren't confused...

wmertens avatar Oct 07 '22 13:10 wmertens

While waiting for a better solution, I have updated the tutorial #1817

forresst avatar Oct 20 '22 15:10 forresst

Hi @O-Hooman , @wmertens , @csdswarm and @forresst 👋 Should we keep this bug open or would it make sense to create a new feature request issue for it?

zanettin avatar Feb 12 '23 21:02 zanettin

@zanettin yea it's more make sense if create it as a request for a new feature

O-Hooman avatar Feb 13 '23 20:02 O-Hooman

Thanks for your reply @O-Hooman 🙏 would you like to formulate it as an enhancement and close this issue afterwards? thanks a lot for your help 🙏

zanettin avatar Feb 13 '23 20:02 zanettin