Svelte APP with the Star Wars API Pt 3

Note: I’m starting off at the end of Part 2.

One of the reasons that component type programing has become so popular is that it makes it very easy to reuse code.

A common use is making a card that can be used multiple times, once for each item in a list. That’s what we’ll be doing in this part. We’ll design a card that will accept a Person object and display it. Using a loop, we’ll have Svelte go through the Persons to display the cards on the screen.

I’m calling it a card, but the component could be a row in a table, or a round badge.

Person.svelte

I’m sure you know what’s first. Go into the components folder and create a file called Person.svelte. Put the following in the file.

<script>
  export let person = {};
</script>

<div class="person">
  <h2>{person.name}</h2>
  <div class="stat">
    <p>Birth Year: {person.birth_year}</p>
    <p>Height: {person.height}</p>
  </div>
</div>

<style>
    .person {
        border: 1px solid #111;
        box-shadow: 2px 2px 3px #444;
        margin: 10px;
        width: 300px;
        text-align: center;
    }
    .stat p {
        margin: 4px;
    }
</style>

Svelte is using export to pass a value from the parent component, in this case from PersonList to the child component, Person. When the Person component is declared in the parent, Person will expect an attribute with the name of person. We’re setting a default value of {}. We don’t need to have a default, but just in case the component declaration doesn’t have an attribute of person, then default is an empty object.

In the HTML portion we are splitting the person object into multiple elements by using the {} braces.

The CSS is styling the component. Everything is nicely packaged in a single file.

Updating PersonList.svelte

Now to use this new component. At first we’ll just be passing a single person through, just to make sure everything is working as it should.

In the script tag add this line. While you can put it anywhere in the script tag, it’s normally placed at the top of the script tag.

import Person from './Person.svelte';

Inside the {:then person} portion of the HTML section replace with:

{:then persons}
    <Person person={persons.results[0]} />
{:catch}

And for the styling, lets style this up.

    .personList {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
    }

Running

Let’s make sure it’s working as expected.

npm run dev

There you go, Luke Skywalker for the win!

Looping through the array

The {:then} section is going to look like this.

{#then persons}
    {#each persons.results as person}
        <Person {person}/>
    {/each}
{:catch}

First thing you might notice is that we’re not saying person={person}. Instead it’s the shorthand version. If both sides are the same name you don’t need to say it twice.

Going back to the site, we’ll notice it now shows ten people.

Adding more to Person.svelte

Having just the Birth Year and Height is a bit simple. Let’s add some more to make it more useful. Inside of Person.svelte, in the HTML section, let’s modify it to this.

  <div class="stat">
    <p>Birth Year: {person.birth_year}</p>
    <p>Height: {person.height}</p>
    <p>Mass: {person.height}</p>
    <p>Homeworld: {person.homeworld}<p>
  </div>

Ok, now it looks better… What happened? There is a URL for the homeworld.

The way this API handles the homeworld is to provide a URL that you call to get the information. The advantage is if you don’t need the homeworld, the person data packet is much smaller and faster. And when you need it, you can just call it. Let’s call it, shall we.

Calling the API for the homeworld information

I mentioned before that StarWarsStore would get more functions, and here we are adding another.

Let’s go to StarWars.js and add a new function to the StarWarsStore object.

    getByURL: (URLString) => {
            let apicall = fetch(URLString)
            .then(function(response) {
                    if (!response.ok) {
                            throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    return response.json();
                    })
            .then(data => data);
            return apicall;
            },

When this is added to the object, don’t forget to add a , at the end of the getAllPersons function.

The difference between the two functions is very small. This new function takes a URLString that is used in fetch. So why not just use one function and pass the URL in all the time? I prefer having the functions specific. This way while reading the code, the functions tell you what you doing. getAllPersons reads very well.

We also want to keep as much inside the module as we can. The URLs are in the module in the CATEGORY and when we use the getByURL function, we’ll only pass in URLs provided by the API.

Calling the Homeworld

We need to decide where we are going to make the call. If we make the call from PersonList.svelte we’d need to pass both the Person information and the Homeworld information down. Since each person has their own homeworld, we’ll have Person.svelte make a call.

Let’s go to Person.svelte.

In the script let’s import StarWarsStore so it we can use the it:

import StarWarsStore from '../store/StarWars';

We’ll also add this to the script. We put it after we declare the person object so we have the homeworld URL.

let HomeWorldInfo = StarWarsStore.getByURL(person.homeworld);

Just like before, StarWarsStore.getByURL() is returning a promise. We can’t just use it until it is fulfilled. We’ll need to use {#await} just like before.

Replace the <p>Homeworld: {person.homeworld}</p> with the following:

    {#await HomeWorldInfo}
        <p>Homeworld: ....</p>
    {:then homeworld}
        <p>HomeWorld: {homeworld.name}</p>
        <p>HomeWorld Population: {homeworld.population}</p>
    {:catch error}
        <p>Whoops, there was an error! {JSON.stringify(error)}</p>
    {/await}

In this case you might not want to show an error or the loading text. We should be able to assume that since we’ve made it this far in, the API is responding. We will have other parts of the Person information.

It’s your call what should happen. Maybe you could add a GIF or some CSS to animate the loading text.

Next up

Remember the Next and Previous? We’re going to add a couple of buttons so we can page through the people.

Resources

GitHub Repo for Svelete-Star Wars API