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

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Joe Berthelot
  • 467
  • 2
  • 4
  • 16
  • Try echoing out the `posts.length` in your template to see what it is. I wonder if you are running into an async issue. – Taplar Apr 08 '20 at 16:46
  • @Taplar interesting... logging `response.length` returns `5`, but logging `posts.length` after it is exported returns `Cannot read property 'length' of undefined`. – Joe Berthelot Apr 08 '20 at 16:51
  • Or do `console.log(JSON.stringify(posts, null, 2))` instead. See [weird array behaviour in javascript](https://stackoverflow.com/q/49838597/215552) for more about what that "i" shows when you hover over it. – Heretic Monkey Apr 08 '20 at 16:52
  • @HereticMonkey that actually returned everything perfectly, in an array...interesting. It is a string, but close. – Joe Berthelot Apr 08 '20 at 16:54
  • 2
    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 = [];`? – Heretic Monkey Apr 08 '20 at 17:00
  • @HereticMonkey making that change actually triggers `catch` in the statement above. – Joe Berthelot Apr 08 '20 at 17:04
  • Shows you what I know :). I'll let the svelte experts take over. Good luck. – Heretic Monkey Apr 08 '20 at 17:06
  • 2
    @HereticMonkey actually, that worked! I had a typo causing the catch. Thank you! – Joe Berthelot Apr 08 '20 at 17:16

4 Answers4

15

Changing:

export let posts;

to

export let posts = [];

fixed the issue. Thanks to @Heretic Monkey

Joe Berthelot
  • 467
  • 2
  • 4
  • 16
  • 1
    [The tutorial](https://svelte.dev/tutorial/onmount) 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. – M Imam Pratama Oct 23 '22 at 07:13
  • Note to self: When using [`{#await}`](https://svelte.dev/tutorial/await-blocks), [use a dummy promise instead of `[]`](https://www.reddit.com/r/sveltejs/comments/jpfav6/comment/itudabu/?utm_source=share&utm_medium=web2x&context=3). – M Imam Pratama Oct 26 '22 at 11:48
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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
RAZ0229
  • 193
  • 4
  • 14
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": "abc@gmail.com"
        },
        "organizer": {
            "email": "m04@group.calendar.google.com",
            "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": "5rqophoiosktsnibmno@google.com",
        "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": "abc@gmail.com"
        },
        "organizer": {
            "email": "a2gopnm04@group.calendar.google.com",
            "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": "78flukukccb2go6asrlki16ola@google.com",
        "sequence": 0,
        "eventType": "default"
    }
]

}

Irhutchi
  • 11
  • 3
0

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

const array = [];
let newArray = [e.detail, ...array];