#Fetching an array of data from a Strapi component

1 messages · Page 1 of 1 (latest)

mystic craterBOT
#

Hi @rustic lake. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

The error message "TypeError: Cannot read properties of undefined (reading 'map')" typically occurs when you try to call the map() function on a variable that is undefined. In your case, it seems like relatedServices is undefined.

Looking at your JSON data, it seems like the relatedServices should correspond to the services array inside the content array. However, in your Astro component, you're trying to access relatedServices directly from Astro.props, which might not be correctly set.

You should ensure that the relatedServices prop is being correctly passed to your Astro component. It should be the services array from your JSON data. Here's an example of how you might extract this data:

const relatedServices = data.attributes.content[0].services.data;

Then, you should pass relatedServices as a prop to your Astro component:

<RelatedServices serviceCardTitle="Some Title" :relatedServices="relatedServices" />

In your Astro component, you should then be able to map over relatedServices as you're currently doing.

If you're still having issues, you might want to add some debugging to your Astro component to check the value of relatedServices:

console.log(relatedServices);

This will print the value of relatedServices to the console, which can help you debug the issue.

#

Remember, all data in Astro components is fetched when a component is rendered. Your deployed Astro site will fetch data once, at build time. In dev, you will see data fetches on component refreshes. If you need to re-fetch data multiple times client-side, use a framework component (https://docs.astro.build/en/core-concepts/framework-components/) or a client-side script (https://docs.astro.build/en/guides/client-side-scripts/) in an Astro component. source (https://docs.astro.build/en/guides/data-fetching/)

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@rustic lake kapa.ai is still learning and improving, please let us know how it did by reacting below

rustic lake
#

@mystic crater
Okay, so once I am in Content.astro I now have…

export interface Props {
  col?: string;
  data?: any;
  serviceCardTitle?: string;
}

const { col, data, serviceCardTitle} = Astro.props as Props;

const relatedServices = data.services;

Problem is, console.log(data); shows…

{ id: 1, __component: 'content.related-service' },

So services isn't there to be called?
The data sauce is coming from ./pages/web-design-services/[slug] where the following is calling the data…

export async function getStaticPaths() {
  const strapiPage = await apiConnect('services', null, 'populate=*');

  return strapiPage.data.map((post) => {
    return {
      params: { slug: post.attributes.slug },
      props: { post: post.attributes },
    };
  });
}

const { post } = Astro.props;

And this is passed into Content as such…

      <Content
        data={ post.content }
        serviceCardTitle={ post.serviceCardTitle }
        col={ `col-12 col-laptop-1-10` }
      />

Any ideas?

mystic craterBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Based on the information you've provided, it seems like the services data is nested within the content array in your JSON data. However, in your getStaticPaths function, you're only passing post.attributes to the props of your Astro component, which doesn't include the services data.

Here's how your JSON data is structured:

{
  "data": {
    "id": 6,
    "attributes": {
      "content": [
        {
          "id": 1,
          "__component": "content.related-service",
          "services": {
            "data": [
              {
                "id": 2,
                "attributes": {
                  "title": "Web Development",
                  "etc": "…",
                }
              },
              {
                "id": 3,
                "attributes": {
                  "title": "Website Support & Maintenance",
                  "etc": "…",
                }
              }
            ]
          }
        }
      ]
    }
  }
}

In your getStaticPaths function, you're only passing post.attributes to the props of your Astro component:

export async function getStaticPaths() {
  const strapiPage = await apiConnect('services', null, 'populate=*');

  return strapiPage.data.map((post) => {
    return {
      params: { slug: post.attributes.slug },
      props: { post: post.attributes },
    };
  });
}

This means that post.attributes only includes the content array, but not the services data inside it.

To include the services data, you need to extract it from the content array and pass it as a separate prop to your Astro component. Here's how you might do this:

export async function getStaticPaths() {
  const strapiPage = await apiConnect('services', null, 'populate=*');
#

return strapiPage.data.map((post) => {
const services = post.attributes.content[0].services;
return {
params: { slug: post.attributes.slug },
props: { post: post.attributes, services },
};
});
}


Then, in your Astro component, you can access `services` from `Astro.props`:

```javascript
const { col, data, serviceCardTitle, services } = Astro.props as Props;

Now, services should include the services data from your JSON data.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@rustic lake kapa.ai is still learning and improving, please let us know how it did by reacting below

rustic lake
#

@mystic crater
Okay, taking onboard what you said, I adjusted my code…
./pages/web-design-services/[slug]

export async function getStaticPaths() {
  const strapiPage = await apiConnect('services', null, 'populate=*,content.services,featuredImage,metaDetails,');

  return strapiPage.data.map((post) => {
    return {
      params: { slug: post.attributes.slug },
      props: { post: post.attributes },
    };
  });
}

const { post } = Astro.props;

Console logging post, now outputs…

    {
      id: 1,
      __component: 'content.related-service',
      services: [Object]
    },

Based on this, any more insight?

mystic craterBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Great, it seems like you're now correctly fetching the services data. The services data is an array of objects, as indicated by [Object] in your console log.

Now, you should be able to pass this services data to your Astro component and map over it. Here's how you might do this:

In your ./pages/web-design-services/[slug] file, pass services to your Astro component:

<Content
  data={{ post.content }}
  services={{ post.content[0].services }}
  serviceCardTitle={{ post.serviceCardTitle }}
  col={{ `col-12 col-laptop-1-10` }}
/>

Then, in your Astro component, you can access services from Astro.props and map over it:

---
export interface Props {
  col?: string;
  data?: any;
  services?: any;
  serviceCardTitle?: string;
}

const { col, data, services, serviceCardTitle } = Astro.props as Props;
---
<section>
  <div class="col-4">
    <p class="h2">Related {serviceCardTitle} services</p>
  </div>
  {services.map((service) => {
    return (
      <div class="col-4">
        <a
          class="h3 light"
          href={`/web-design-services/${service.data.attributes.slug}`}
          title={service.data.attributes.title}
        >
          {service.data.attributes.title}
        </a>
      </div>
    );
  })}
</section>

This should correctly map over your services data and create a link for each service.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@rustic lake kapa.ai is still learning and improving, please let us know how it did by reacting below

rustic lake
#

@mystic crater
Almost there, I now have…
src/components/organisms/Content.astro

import RelatedService from '../molecules/RelatedService.astro';
export interface Props {
  col?: string;
  data?: any;
  serviceCardTitle?: string;
  services?: any;
}

const { col, data, serviceCardTitle, services} = Astro.props as Props;
---
<>
  { data ? data.map((component) => {
    switch (component.__component) {
      case 'content.related-service':
        return (
          <RelatedService
            serviceCardTitle={ serviceCardTitle }
            services={ services }
          />
        );
      default:
        return 'Nothing Found';
    }
    })
  : null }
</>

However, services does not appear to be passed to ../molecules/RelatedService.astro?

../molecules/RelatedService.astro looks as such…

---
export interface Props {
  serviceCardTitle?: string;
  services?: any;
}

const { serviceCardTitle, services } = Astro.props as Props;
---
<section>
  <div class="row">
    <div class="col-12 col-laptop-1-10">
      <div class="sub-row top related-service">
        <div class="col-4">
          <p class="h2">Related { serviceCardTitle } services</p>
        </div>
          { services.map((service) => {
            return (
              <div class="col-4">
                <a
                  class="h3 light"
                  href={`/web-design-services/${service.data.attributes.slug}`}
                  title={service.data.attributes.title}
                >
                  {service.data.attributes.title}
                </a>
              </div>
            );
          }) }
      </div>
    </div>
  </div>
</section>
mystic craterBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Based on your code, it seems like you're correctly passing the services prop from your Content.astro component to your RelatedService.astro component. However, you're getting the services data from Astro.props in both components, which might be causing the issue.

In your Content.astro component, you're getting services from Astro.props:

const { col, data, serviceCardTitle, services} = Astro.props as Props;

Then, you're passing services to your RelatedService.astro component:

<RelatedService
  serviceCardTitle={ serviceCardTitle }
  services={ services }
/>

In your RelatedService.astro component, you're again getting services from Astro.props:

const { serviceCardTitle, services } = Astro.props as Props;

This means that services in your RelatedService.astro component is the same as services in your Content.astro component, which might not be what you want.

Instead, you should get the services data from the component object in your Content.astro component, and then pass this data to your RelatedService.astro component. Here's how you might do this:

In your Content.astro component:

<RelatedService
  serviceCardTitle={ serviceCardTitle }
  services={ component.services }
/>

Then, in your RelatedService.astro component, you can map over services as you're currently doing.

If you're still having issues, you might want to add some debugging to your RelatedService.astro component to check the value of services:

console.log(services);

This will print the value of services to the console, which can help you debug the issue.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@rustic lake kapa.ai is still learning and improving, please let us know how it did by reacting below

rustic lake
#

@mystic crater
RelatedServices.astro now has the following…

export interface Props {
  serviceCardTitle?: string;
  services?: any;
}

const { serviceCardTitle, services } = Astro.props as Props;
console.log(services);

services returns…

    {
      id: 1,
      __component: 'content.related-service',
      services: [Object]
    },

However, when the following is called…

        { services.map((service) => {
          return (
            <div class="col-4">
              <a
                class="h3 light"
                href={`/web-design-services/${service.data.attributes.slug}`}
                title={service.data.attributes.title}
              >
                {service.data.attributes.title}
              </a>
            </div>
          );
        }) }

I get the following error…

TypeError: services.map is not a function
    at eval (/Users/grant/Sites/Development/doddle-company-astro-site/src/components/molecules/RelatedService.astro:14:426)
mystic craterBOT
#

Let me check the knowledge base for you. One moment! :books:

#

The error message "TypeError: services.map is not a function" typically occurs when you try to call the map() function on something that is not an array. In your case, it seems like services is not an array, but an object.

Looking at your console log, services is an object that contains an id, a __component, and a services property:

{
  id: 1,
  __component: 'content.related-service',
  services: [Object]
}

The services property of this object seems to be the array you want to map over. You should therefore access this array as services.services in your Astro component:

{ services.services.map((service) => {
  return (
    <div class="col-4">
      <a
        class="h3 light"
        href={`/web-design-services/${service.data.attributes.slug}`}
        title={service.data.attributes.title}
      >
        {service.data.attributes.title}
      </a>
    </div>
  );
}) }

This should correctly map over your services data and create a link for each service.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@rustic lake kapa.ai is still learning and improving, please let us know how it did by reacting below

rustic lake
#

@mystic crater
I think this should be services.data based on the json returned…

        {
          "id": 1,
          "__component": "content.related-service",
          "services": {
            "data": [
              {
                "id": 2,
                "attributes": {
                  "title": "Web Development",
                }
              },
              {
                "id": 3,
                "attributes": {
                  "title": "Website Support & Maintenance",
                }
              }
            ]
          }
        },

But I still get an error… TypeError: Cannot read properties of undefined (reading 'attributes')

mystic craterBOT
#

Let me check the knowledge base for you. One moment! :books: