Can you conditionally wrap a custom element's slot in a parent element?

When I’m working on a custom element that has a slot, sometimes I’d like to wrap the slot in a parent element. For example, in this component the slot element is a child of card-body:

card.html:

<template>
    <card-header>
        ${title}
    </card-header>
    <card-body>
        <slot></slot>
    </card-body>
</template>

index.html:

<card title="My Card">
    <h1>Some content</h1>
</card>

Now suppose I have a second component that I already use which has its own card-body and card-footer components:

my-form.html:

<template>
    <card-body>
        <slot></slot>
    </card-body>
    <card-footer>
        <button>Submit</button>
    </card-footer>
</template>

Since my-form has a footer, I don’t want to put it inside the card-body of card. Is there a way to conditionally omit the card-body tag in card.html? Something like:

<card title="My Card" no-body="true">
    <my-form>
        <input /> etc.
    </my-form>
</card>

when using the template and:

card.html:

<template>
    <card-header>
        ${title}
    </card-header>
    <card-body (include this wrapping tag if no-body is false)>
        <slot></slot> (but include this always)
    </card-body>
</template>

Thanks for any and all help! (I accidentally submitted the post before I was done writing… it’s complete now)

1 Like

Sounds like a conditional containerless or as-element but Im honestly not sure as I rarely use those features. @bigopon this might be one for you.

Oh and btw welcome @zshall :wink:

2 Likes

You could do this with processContent()
@bigopon has a good writeup here
If you need help, set up your non-working example on codesandbox, post it here, and I will give it a shot this weekend

2 Likes

Thanks for the welcome and the recommendations!! I’m reading through @bigopon’s post and the rest of the thread now and will try a few things out. processContent is new to me so I’ll take some time to learn it. If I figure it out I’ll post my solution. Otherwise I’ll post the non-working example.

2 Likes

I’ve been working at it for the last few hours and am a bit stumped. Here’s what I’ve got so far:

Code Sandbox link

When I was reading through the thread my first thought would be to remove the card-body wrapper (while keeping its children) using the node parameter of processContent. I imagined that processContent would give me the whole custom element in node but realized it gives me the slot contents only. Calling node.parentNode doesn’t seem to work either.

My second idea was to go into the node and unwrap the first direct child which is a card-body element. This didn’t work since the slot contents aren’t compiled at this point and my form-base component uses a nested card-body which hasn’t been compiled yet.

Will continue to work on this.

The end result I’m looking for is that when I have form-base as a child of tab, only one card-body element will be used.

Here’s an image that shows what I’m trying to accomplish:

There may be other, better ways to deal with this problem that I haven’t thought of yet too.

Thanks for all the help so far :slight_smile:

1 Like

A small note for your form buttons: you need to clearly define which buttons are able to submit your form, and which are not. Button inside a form will have type=submit, which could be very undesirable.
So it should look like this in your form base:


  <card-footer>
    <button type=submit>Submit</button>
    <button type=button>Back</button>
  </card-footer>

About what you want to achieve, it seems to me if you define card-body in each of the element, you should have card-body, it’s expected. Maybe change how you compose your tab? Normally I would advise against needlessly creating custom element, in your case, it’s the <form-base/>. Aurelia, with its close to bare html characteristic, and many powerful binding should give you an easy time dealing with big big html view, unlike other html in JSS frameworks.

2 Likes

What if you just style it so that card-body’s inside a card-body does not have border?
Like this: https://codesandbox.io/s/component-wrapping-x92w0

2 Likes

As for actually “conditionally wrap a slot in a parent element”, I hoped we could do it with

<compose view="card/card-body.html">
    <slot></slot>
</compose>

Unfortunately that didn’t work because the content of <slot> is rendered into the view BEFORE the content of <card-body>.

I also tried

<section as-element.bind="omitBody ? 'div' : 'card-body'">
    <slot></slot>
</section>

and of course that didn’t work either because as-element doesn’t seem to be bindable. (as-element="${...}" didn’t work either.)

2 Likes