Matthew Moisen
Matthew Moisen

Reputation: 18299

Svelte How to Conditionally Display wrapping element in Parent depending on Child component's variable?

I have a child Button that I want to reuse. This button contains the code to check whether the user has permissions to use the Button. In one parent, I just want to display the button as is, and this works fine. However in a different component, I want to wrap the button in a Card, and conditionally display both the Card and the Button depending on whether the user is authorized.

Child Component:

<script>
    let isAuthorized = false;

    async function updateIsAuthorized() {
        isAuthorized = await hasPermission('CREATE_FOO');
    }

    onMount(isAuthorized);
</script>

{#if isAuthorized}
    <Button>...</Button>
{/if}

The following Parent of course will display an empty Card if the user doesn't have permissions to use the Button. Instead, I want to hide the Card in this case, so this is no good:

<script>
    import FooButton from './FooButton.svelte';
    // ...

</script>

<Card>
    <FooButton>
</Card>

I did try adding an isAuthorized boolean to the parent (and export let isAuthorized to the child), and binding it, but this does not work:

<script>
    import FooButton from './FooButton.svelte';
    // ...
    let isAuthorized = false;
</script>

{#if isAuthorized}
    <Card>
        <FooButton bind:isAuthorized>
    </Card>
{/if}

I assume the problem here is that becuase isAuthorized default to false, the FooButton is never rendered, so the child component never has a chance to modify the bound isAuthorized.

Upvotes: 0

Views: 853

Answers (1)

Stephane Vanraes
Stephane Vanraes

Reputation: 16451

I don't think there is a really elegant solution to this, as you would be wrapping the button based on a state that is internal to this button.

The only way I see for solving this is to flip the entire thing around and extend your Button component to be able to render the Card itself (depending on some flag perhaps).

<script>
  export let isCard = false;
  let isAuthorized
</sccript>

{#if isAuthorized}
  {#if isCard}
    <Card>
     <button>...</button>
    </Card>
  {:else}
    <button>...</button>
  {/if}
{/if}

You could make this slightly more dynamic if you allow passing in a component as the 'Card'

<script>
  export let cardComponent;
  let isAuthorized
</script>

{#if isAuthorized}
  {#if cardComponent}
    <svelte:component this={cardComponent}>
      <button>...</button>
    </svelte:component>
  {:else}
     <button>...</button>
  {/if}
{/if}

In the last case the usage would be something like:

<script>
  import Button from '...';
  import Card from '...';
</script>

<Button cardComponent={Card} />

with Card the card you want to show, with a <slot> element where the button should go.

Upvotes: 0

Related Questions