Error: {#each} only iterates over array-like objects. -- Javascript & Svelte
Asked Answered
U

4

13
<script context="module">
    import GhostContentAPI from '@tryghost/content-api';

    // const api = 'http://localhost/posts';
    const api = new GhostContentAPI({
        url: 'http://localhost',
        key: '95a0aadda51e5d621abd2ee326',
        version: "v3"
    });

    export async function preload({ params, query }) {
        try {
            const response = await api.posts.browse({ limit: 5, fields: 'title, slug' });
            return {
                posts: response
            }
        } catch(err) {
            console.log('Error');
        }
    }
</script>

<script>
    export let posts;
</script>

<svelte:head>
    <title>Blog</title>
</svelte:head>

<h1>Recent posts</h1>
<ul>
    {#each posts as post}
        <li>
            <a rel='prefetch' href='blog/{post.slug}'>{post.title}</a>
        </li>
    {/each}
</ul>

I'm using vanilla JavaScript and Svelte to simply fetch a list of blog posts, which are objects from the Ghost Blog Rest API. The Ghost API function works fine and pulls the correct objects, but the problem begins when trying to use Svelte's {#each} block to display each object because they aren't in an array and I cannot figure out how to fix it. Here's the exact error message in the console:

Error: {#each} only iterates over array-like objects.

Writing a console.log(response) after the const response declaration outputs the attached image, but only if I comment out the {#each} block first.

I'm guessing I simply need to move the 5 objects into an array, but I also don't understand why the console.log above only works when the HTML is commented out.

Console Log Image

Urbain answered 8/4, 2020 at 16:43 Comment(8)
Try echoing out the posts.length in your template to see what it is. I wonder if you are running into an async issue.Taal
@Taal interesting... logging response.length returns 5, but logging posts.length after it is exported returns Cannot read property 'length' of undefined.Urbain
Or do console.log(JSON.stringify(posts, null, 2)) instead. See weird array behaviour in javascript for more about what that "i" shows when you hover over it.Malmo
@HereticMonkey that actually returned everything perfectly, in an array...interesting. It is a string, but close.Urbain
Depends on where you run the code, I expect. For instance, run that just before your {#each} in the template and you may get something different. I don't know svelte or ghost or sapper, but typically there's a way of telling the template that the array will be filled asynchronously. Maybe if you just did export let posts = [];?Malmo
@HereticMonkey making that change actually triggers catch in the statement above.Urbain
Shows you what I know :). I'll let the svelte experts take over. Good luck.Malmo
@HereticMonkey actually, that worked! I had a typo causing the catch. Thank you!Urbain
U
17

Changing:

export let posts;

to

export let posts = [];

fixed the issue. Thanks to @Heretic Monkey

Urbain answered 8/4, 2020 at 17:19 Comment(3)
The tutorial does "suggests" initializing soon-to-be-array variables with [] although it doesn't give a clue why we should do it nor it gives any warning for not doing it.Delldella
Note to self: When using {#await}, use a dummy promise instead of [].Delldella
That works if you have an array as import, but if you have a structure with the array within it, you need to force the array when undefined right inside the each statement: ~~~ {#each importedThing.fields || [] as field, i} {field.fieldName} {/each}Brute
H
2

I was facing a similar issue. This happened while working with Firebase Firestore and by comparing the erroneous value with the prototype given in the official svelte documentation, I came to a conclusion.

enter image description here

So I simply used the javascript Object.values() method on the retrieved object and {#each} only iterates over array-like objects error was gone.

Hung answered 19/5, 2021 at 17:34 Comment(0)
P
1

I was having a similar issue and stumbled upon this post. It would be nice to see the full JSON repsonse in the original post to give greater context. In my case it turns out I was referring to nested array objects incorrectly. I hope this helps anyone who has a similar issue.

Doesn't Work

 <tbody class="uk-text-left">
    <!-- {@debug calendarEventList} -->
    {#if calendarEventList}
      {#each calendarEventList as event}
        <tr>
            <td>{calendarEventList.items[1].summary}</td>
            <td>{calendarEventList.items[1].start.dateTime}</td>
            <td>{calendarEventList.items[1].end.dateTime}</td>
            <td>{calendarEventList.items[1].creator.email}</td>
            
        </tr>
      {/each}
    {/if}
  </tbody>

Uncaught (in promise) Error: {#each only iterates over array-like objects

Does Work

 <tbody class="uk-text-left">
    <!-- {@debug calendarEventList} -->
    {#if calendarEventList}
      {#each calendarEventList.items as event}
        <tr>
            <td>{event.summary}</td>
            <td>{event.start.dateTime}</td>
            <td>{event.end.dateTime}</td>
            <td>{event.creator.email}</td>
        </tr>
      {/each}
    {/if}
  </tbody>

Example of get request to google calendar api

{
"kind": "calendar#events",
"etag": "\"p334fwehgbta5ve0g\"",
"summary": "Svelte StackOverflow",
"updated": "2022-04-11T10:44:38.077Z",
"timeZone": "Europe/Dublin",
"accessRole": "reader",
"defaultReminders": [],
"nextSyncToken": "CMj8_xxxx_cCEAAHSHDHD8-LRAQ==",
"items": [
    {
        "kind": "calendar#event",
        "etag": "\"3299347666786000\"",
        "id": "5rqm5kq33ghd9tuhsktjhoisdvho",
        "status": "confirmed",
        "htmlLink": "https://www.google.com/calendar/event?eid=Npw",
        "created": "2022-04-11T10:43:53.000Z",
        "updated": "2022-04-11T10:43:53.393Z",
        "summary": "Call Back Customer",
        "creator": {
            "email": "[email protected]"
        },
        "organizer": {
            "email": "[email protected]",
            "displayName": "Svelte StackOverflow",
            "self": true
        },
        "start": {
            "dateTime": "2022-04-11T07:15:00+01:00",
            "timeZone": "Europe/Dublin"
        },
        "end": {
            "dateTime": "2022-04-11T08:15:00+01:00",
            "timeZone": "Europe/Dublin"
        },
        "iCalUID": "[email protected]",
        "sequence": 0,
        "eventType": "default"
    },
    {
        "kind": "calendar#event",
        "etag": "\"3299347756154000\"",
        "id": "78flukdyhjjki16ola",
        "status": "confirmed",
        "htmlLink": "https://www.google.com/calendar/event?eid=NzhjAZw",
        "created": "2022-04-11T10:44:25.000Z",
        "updated": "2022-04-11T10:44:38.077Z",
        "summary": "Maintenance at pharma company x",
        "creator": {
            "email": "[email protected]"
        },
        "organizer": {
            "email": "[email protected]",
            "displayName": "Svelte StackOverflow",
            "self": true
        },
        "start": {
            "dateTime": "2022-04-14T08:00:00+01:00",
            "timeZone": "Europe/Dublin"
        },
        "end": {
            "dateTime": "2022-04-14T09:00:00+01:00",
            "timeZone": "Europe/Dublin"
        },
        "iCalUID": "[email protected]",
        "sequence": 0,
        "eventType": "default"
    }
]

}

Patman answered 11/4, 2022 at 15:50 Comment(0)
B
0

This happens in Dispatching too. Solution is not reassigning array, the solution is something like this :

const array = [];
let newArray = [e.detail, ...array];
Bogoch answered 13/10, 2020 at 22:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.