#Render the first 3 passed items in a different location than the following passed items with slots?

66 messages · Page 1 of 1 (latest)

dry jolt
#

So I have a component ImageCardSection which looks like this:

---
const { id, heading } = Astro.props;
---

<section {id}>
    <h2 class="text-xl">{heading}</h2>

        <ul class="mt-2 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
            <slot /> // <--- Render first three items here
        </ul>

        <details>
            <summary>Show all</summary>

            <ul>
                <slot /> // <--- And the rest here
            </ul>
        </details>
</section>

I pass these image cards in for the slots:

<ImageCard logo="https://picsum.photos/id/30/200/100" href="https://google.com" label="Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestiae, repudiandae" />

My question is: Could I somehow render the first three ImageCards inside the top ul of my ImageCardSection, and all the folowing inside the ul of the details tag?

#

With this current layout

#

(I could use react or svelte, but I thought astro only)

carmine sky
#

Hey @dry jolt you can indeed do this,

#

its best to take advantage of using named slots if you have more than one <slot> in the component,

dry jolt
carmine sky
#

two secs ill get you some guidance

#

well if you are passing in them in as children then aye

dry jolt
carmine sky
#

well you can create a wrapper for the first three so they pass in as children, then pass through the others into the names, lets see if I can sketch this out

topaz scaffold
carmine sky
#
---
const { id, heading } = Astro.props;
---

<section {id}>
    <h2 class="text-xl">{heading}</h2>

        <ul class="mt-2 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
            <slot name="primary"/> // <--- Render first three items here
        </ul>

        <details>
            <summary>Show all</summary>

            <ul>
                <slot name="rest"/> // <--- And the rest here
            </ul>
        </details>
</section>
//parent
---
import Gallery from 'Gallery.astro'
---
<Gallery>
 {Array.from(mainImgSet).map((item)=><li slot="primary"><img src=`"${item.src}"`/></li>}
{Array.from(restOfImgSet).map((item)=><li slot="rest"><img src=`"${item.src}"`/></li>}
</Gallery>
dry jolt
topaz scaffold
# dry jolt Am I doing something wrong?

Sorry .render() returns a promise so it need an await before it
const slot = (new DOMParser).parseFromString(await Astro.slots.render('default'), "text/html");

dry jolt
#

Yeah, also works without an additional variable

topaz scaffold
dry jolt
#

And how do I work with it now inside the html?

topaz scaffold
#

Now you can do stuff like slot.querySelector('.image-card')

#

Then render the first three of that array in the first list and the rest inside your dialog

dry jolt
#

For some reason it tells me that DOMParser is not defined

#

I don't see a mistake here

#

Do I have to import it somehow for astro

topaz scaffold
#

Im just realising that DOMParser is a browser API so you would have to use a Node package like jsdom https://www.npmjs.com/package/jsdom to do the same thing

dry jolt
#

I'm going to install that quickly

topaz scaffold
#

const slot = (new JSDOM(await Astro.slots.render('default'))).window.document
slot.querySelector('.image-card')

dry jolt
#

This is what I'm doing now:

---
const { id, heading } = Astro.props;

import jsdom from "jsdom";
const { JSDOM } = jsdom;
const slot = (new JSDOM(await Astro.slots.render('default'))).window.document
slot.querySelectorAll('li');
---

<section {id}>
    <h2 class="text-xl">{heading}</h2>

        <ul class="mt-2 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
            {slot[0]}
        </ul>

        <details>
            <summary>Show all</summary>

            <ul class="mt-2 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
            </ul>
        </details>
</section>
#

It doesn't render the li yet

topaz scaffold
#

You probably have to turn the elements (slot[0]) back into a string and use <Fragment set:html={}>

#

I was trying to follow this example in their docs ```js
const frag = JSDOM.fragment(<p>Hello</p>);
console.log(frag.firstChild.outerHTML); // logs "<p>Hello</p>"

But I cant get it working
topaz scaffold
#

I finally figured it out ```jsx

import { JSDOM } from 'jsdom';

const { id, heading } = Astro.props;

const slot = [...JSDOM.fragment(await Astro.slots.render('default')).children]

<section {id}>
<h2 class="text-xl">{heading}</h2>
<ul class="mt-2 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{ slot.slice(0, 3).map(element => {
<Fragment set:html={element.outerHTML}/>
})}
</ul>
<details>
<summary>Show all</summary>
<ul>
{ slot.slice(3).map(element => {
<Fragment set:html={element.outerHTML}/>
})}
</ul>
</details>
</section>

dry jolt
#

Oh, so you can map over it. Yeah, that makes sense.

#

To have an iterable array for map

topaz scaffold
#

Turns it to an array

#

Ya

dry jolt
#

Let me try it out 🙂

topaz scaffold
#

🤞

dry jolt
#

Oof, it's not working

#

Not rendering any images

topaz scaffold
#

Dang

#

Im going try it in a stackblitz

dry jolt
#

I'm not sure why it doesn't work though. The code you wrote makes sense to me

#

So when I console log the element.outerHTML it shows everything correctly @topaz scaffold

#

But it doesn't render it to the DOM for some reason

topaz scaffold
#

Omg im so dumb

#

I it needs a return before fragment return <Fragment set:html={}/>

dry jolt
#

ohhh

#

yeahhh

#

it's the same as every framework, right xd

#

@topaz scaffold works now :))

topaz scaffold
#

Woohoo

dry jolt
#

I oversaw that return issue because I always use normal brackets to auto return for arrow functions xd

topaz scaffold
#

Im glad it worked this was something I wanted to try doing for awhile so now I know

#

Ya same

dry jolt
#
{ slots.slice(0, 3).map(element => (
                <Fragment set:html={element.outerHTML}/>
            ))}
topaz scaffold
#

You could even make it one line without the ()
{ slots.slice(0, 3).map(e => <Fragment set:html={e.outerHTML}/>)}

dry jolt
topaz scaffold
#

Ya I like that you don't have to pass named slots makes it a bit nicer

dry jolt
#

Well now I could also hide the details tag whenever the slots array length is less than 3, so that's nice as well

topaz scaffold
#

Yes so many more possibilities

dry jolt
#

Yup, works like a charm.

Thank you very much for taking the time 🙂

topaz scaffold
#

Ya no problem, happy coding