#For loops in astro components

9 messages · Page 1 of 1 (latest)

shell sable
#

Hi, I would like to use a "complex" for loop to build my component from an array of typescript objects, but I cannot wrap my head around it. This is my situation:

class Thumbnail {
  category: VideoCategory;
  link: string;
  src: string;
  title: string;
  text: string;

  constructor(
    category: VideoCategory,
    link: string,
    src: string,
    title: string,
    text: string
  ) {
    this.category = category;
    this.link = link;
    this.src = src;
    this.title = title;
    this.text = text;
  }
}

export enum VideoCategory {
  animation = "Animation",
  nature = "Nature",
  sport = "Sport",
  movies = "Movies",
}

Then in my component I would like to do something along the lines of:

---
// imports ...

const thumbnails: Thumbnail[] = [] // populated from imports
---
<Layout>
<div class="container">
{
    for (const category of Object.keys(VideoCategory)) {
        for (const thumbnail of thumbnails) {
            <div>
            <h2>{category}</h2>
            if (thumbnail.category == VideoCategory[category]) {
                <div class="container">
                    <a href={thumbnail.link}>
                    <img src={thumbnail.src} alt="" />
                    <span class="title">{thumbnail.title}</span>
                    <span class="text">{thumbnail.text}</span>
                    </a>
                </div>
            }
            </div>
        }
    }
}
</div>
</Layout>

Is it possible to use if and normal for loops in a component?

hollow crypt
#

try moving the for loop in a function inside the curly braces, and returning JSX from it.

e.g.

<div>
  {() => {
    for (const x of [1, 2]) {
      return <span>hi</span>
    }
  }}
</div>
#

as long as it's a function and you return from it, you can do all sorts of complex stuff

wet oyster
#

Oh you can even turn this into a generator function* if its SSR mode you do get streaming but this also lets you stream in the results of the loop

<div>
  {function* loop(){
      for(const item of list){
        yeild <span>item<span>
      }
   }
</div>

More info about generators can be found here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*

The function* declaration (function keyword
followed by an asterisk) defines a generator function, which returns a
Generator object.

rocky oriole
#

You could also try out astro flow https://www.npmjs.com/package/@astropub/flow

fringe marsh
#

I would recommend:

---
// imports ...

const thumbnails: Thumbnail[] = [] // populated from imports
---
<Layout>
<div class="container">
{Object.entries(VideoCategory).map((categoryKey, categoryValue) => (
  <div>
  <h2>{categoryKey}</h2>
  {thumbnails.filter((thumbnail) => thumbnail.category === categoryValue)
    .map((thumbnail) => (
      <div class="container">
        <a href={thumbnail.link}>
        <img src={thumbnail.src} alt="" />
        <span class="title">{thumbnail.title}</span>
        <span class="text">{thumbnail.text}</span>
        </a>
      </div>)}
  </div>)}
</div>
</Layout>

.map and .filter are great for astro/jsx type mappings!

shell sable
#

Hi everyone! Thank you for the amazing replies.

@hollow crypt I was not able to use your approach to solve my problem. The closest I got (without compilation errors) is the following, but the if (thumbnail.category ...) line is rendered as a string, and not executed.

<Layout>
  <div class="container">
    {
      () => {
        for (const category of Object.keys(VideoCategory)) {
          for (const thumbnail of thumbnails) {
            return (
              <div>
                <h2>{category}</h2>
                if (thumbnail.category == VideoCategory[category])
                {
                  <div class="container">
                    <a href={thumbnail.link}>
                      <img src={thumbnail.src} alt="" />
                      <span class="title">{thumbnail.title}</span>
                      <span class="text">{thumbnail.text}</span>
                    </a>
                  </div>
                }
              </div>
            );
          }
        }
      }
    }
  </div>
</Layout>

@rocky oriole @wet oyster Your approaches are very interesting, I will keep them in mind for future needs.

#

The approach from @fringe marsh was the winning one for me. Here is my working code (you were missing two parentheses and I changed back to my iteration on the keys as yours was giving me a compilation error on the comparison thumbnail.category === categoryValue):

<Layout meta={meta}>
  <div class="container">
    {
      Object.keys(VideoCategory).map((category) => (
        <div>
          <h2>{category}</h2>
          {thumbnails
            .filter(
              (thumbnail) => thumbnail.category === VideoCategory[category]
            )
            .map((thumbnail) => (
              <div class="container">
                <a href={thumbnail.link}>
                  <img src={thumbnail.src} alt="" />
                  <span class="title">{thumbnail.title}</span>
                  <span class="text">{thumbnail.text}</span>
                </a>
              </div>
            ))}
        </div>
      ))
    }
  </div>
</Layout>