TaylorR
TaylorR

Reputation: 4013

Consuming API result in a Svelte component

Newbie to Svelte here but have good knowledge on other languages.

I had an API that returns something like this:

{
    "data": [
        {
            "id": 1997,
            "place": 2,
            "publisher": 4,
            "bookid": "01997",
            "title": "法律简史——人类制度文明的深层逻辑",
            "author": "桑本谦",
            "region": "中国大陆",
            "copyrighter": null,
            "translated": 0,
            "purchdate": "2022-09-29",
            "price": 89,
            "pubdate": "2022-09-01",
            "printdate": "2022-09-01",
            "ver": "1.1",
            "deco": "普通",
            "kword": 490,
            "page": 477,
            "isbn": "978-7-108-07473-7",
            "category": "D909.9",
            "location": null,
            "intro": "费老推荐。一本好书。",
            "instock": 1,
            "img": "http://lumen/books/image/01997/桑本谦/法律简史——人类制度文明的深层逻辑/600"
        }
    ]
}

In my Svelte app, I created a Book.ts to declare the type:

export interface Book {
    id: number,
    bookid: string,
    title: string,
    author: string,
    img: string,
    ...
}

And also a BookManager.ts to retrieve data:

import type {Book} from './Book';

export class BookManager {
    private base="http://lumen";
    async getLatestBook(count=1) : Promise<Book[]>{
        const uri=this.base+'/books/latest/'+count;
        const ret=await fetch(uri);
        const json=await ret.json();
        const data=await json['data'] as Book[];
        
        return data;
    }

}

In my LatestBook.svelte, I try to consume this data like below:

<script lang="ts">
    import { BookManager } from "../lib/BookManager";
    import type { Book } from "../lib/Book";
    
    import { onMount } from "svelte";
    var lb: Book;

    onMount(async () =>{
        const bm=new BookManager();
        let lbs=await bm.getLatestBook(1);
        lb=lbs[0];

        console.log(lb); // This shows the CORRECT data
    })

    
</script>

<div class="col-md-6 col-lg-4 mb-4 development grid-item">
  <a
    href="#!"
    class="text-white bg-dark position-relative d-block overflow-hidden card-hover-2"
  >
    <img src="../../static/img/projects/1.jpg" alt="{lb.title}" class="w-100 img-zoom" />
    <div
      class="card-hover-2-overlay position-absolute start-0 top-0 w-100 h-100 d-flex px-4 py-5 flex-column justify-content-between"
    >
      <div class="card-hover-2-header w-100">
        <div class="card-hover-2-title">
          <h5 class="fs-4 mb-2">{lb.title}</h5>
        </div>

But the page shows a 500 error and the error message is:

TypeError: Cannot read properties of undefined (reading 'title')

What went wrong here? Thanks for your help.

Upvotes: 1

Views: 54

Answers (1)

Corrl
Corrl

Reputation: 7699

The initial value var lb: Book; is undefined so reading the title like this {lb.title} gives the error
One way would be to write {lb?.title ?? ''} ( ?. / ?? )
or use an #if block

<script lang="ts">
    import { BookManager } from "../lib/BookManager";
    import type { Book } from "../lib/Book";    
    import { onMount } from "svelte";

    var lb: Book;

    onMount(async () =>{
        const bm=new BookManager();
        let lbs=await bm.getLatestBook(1);
        lb=lbs[0];
    })    
</script>

{#if lb}
  ...
  <div> 
    <h5 class="fs-4 mb-2">{lb.title}</h5>
  </div>
{/if}

or an #await block

<script lang="ts">
    import { BookManager } from "../lib/BookManager";
    import type { Book } from "../lib/Book";

    async function fetchBook(): Promise<Book> {
        const bm=new BookManager();
        let lbs=await bm.getLatestBook(1);
        return lbs[0];
    })
    
</script>

{#await fetchBook() then lb}
  ...
  <div> 
    <h5 class="fs-4 mb-2">{lb.title}</h5>
  </div>
{/await}

Upvotes: 2

Related Questions