Svelte APP with the Star Wars API Pt 4

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

We’ve got a really nice app going here. But it’s only showing the first 10 characters out the 82 that is in the API. The API does give us the ability to get the next ten. And once there we can go back to the previous.

The plan is we’re going to add two buttons; one for next and one for previous. The buttons will make a call to a function, that will then either load the next or previous based on the button we pressed.

We’ll be working just in the PersonList.svelte file for this feature in our app.

Adding the buttons

For the buttons to have the proper information to work we need to make sure it’s in the right spot. Since it needs the previous and next URLs it needs to be inside the {#await} structure, but not inside the {#each} because the URLs aren’t part of the results (person) array.

Put the following right below the {#each} loop and right before the {:catch error}.

<div class="buttons">
    <button data-link="{persons.previous}" on:click={loadList}>Previous 10</button>
    <button data-link="{persons.next}"on:click={loadList}>Next 10</button>
</div>

For the most part this is just standard buttons. Two parts I want to draw your attention to.

data-link is a way of attaching data to an HTML element. It’s common to add the attribute without use data- but to keep with standards, we should use data-* format. This also makes it easier to select; either by CSS of JavaScript a set of elements that have a data-* element without grabbing every element on the page.

Svelte handles event handlers differently then standard HTML and JavaScript. on:eventName={handler}.

If the click event happens inside a component, don’t put the ={handler} on the element inside the component and it’ll pass upwards to the parent component’s element.

In this case the on:click will call the function loadList.

loadList function

Add this function to the script portion.

    function loadList({ target }){
        if(target.dataset.link != null){
            allPersons = StarWarsStore.getByURL(target.dataset.link);
        }
    }

The function will automatically receive an event object, just like other event handlers. Using object destructuring, we’re pulling out just the target part of the event object since that’s all we need.

The if statement is testing to make sure that there is a link available. Without a URL we know the API call will fail.

Speaking of the API call, we’re using the same function as we did with the homeworld, assigning it into allPersons variable. This is the same variable we used before. The beauty here is that Svelte will handle it the same it did when we first loaded the page. Again the promise will be pending and once fulfilled, the people will show up.

Styling the buttons

I know styling isn’t required, but it does make it look a lot better. So add this to the style section. Be my guest to style this differently if you want.

    .buttons {
        width: 100%;
        text-align: center;
    }
    .buttons button {
        padding: 10px 20px;
        border-radius: 10px;
        border: none;
        outline: none;
        margin: 5px;
        background-color: #8185fc;
        color: #222;
        font-size: 1.5em;
    }

Testing it out

Go ahead and test out the app.

npm dev run

The Previous button doesn’t work until you jump to the the second page. But can you tell before you press the button? That’s a bad user experience. The button should be styled differently when it is disabled so the user can tell.

Styling a disabled button

Using the disabled attribute, we can mark the button as disabled and use a CSS selector to style it while disabled. Replace (or edit) the buttons div so it matches this.

<div class="buttons">
    <button 
        data-link="{persons.previous}" 
        disabled="{(persons.previous) ? '' : 'true'}" 
        on:click={loadList}>
        Previous 10
    </button>
    <button 
        data-link="{persons.next}" 
        disabled="{(persons.next) ? '' : 'true'}"  
        on:click={loadList}>
        Next 10
    </button>
</div>

I’m formatting it out a bit so it’s easier to see what it on the button element. What we’ve added is a disabled attribute with a ternary operator. The ternary operator works in this spot because we wrapped it in {} so Svelte will evaluate it.

The ternary operator uses the format condition ? exprIfTrue : exprIfFalse. What condition are we testing? JavaScript has a concept called truthy and falsy. Everything can be converted to one of these.

What’s falsy? Here is a list of values JavaScript considers falsy.

  • false
  • 0 (zero)
  • ’’ or “” (empty string)
  • null
  • undefined
  • NaN (not a number)

Every other value is truthy.

What is happening in this situation? If persons.next is any of the falsy values then it’s considered falsy and the exprIfFalse is used. For the first page the persons.next would be an empty string and that’s considered false. If there is a URL in persons.next then it would be truthy and the button would not be disabled.

Add this styling to the CSS section:

button:disabled {
    background-color: #8185fc;
    color: #999;
}

Go ahead and test it out. Verify that when you get all the way to the last page that the Next button also becomes disabled.

Up Next…

This is working pretty well. Let’s see about adding a little more. What movies are each person it? We’ll need to add another API to the Person.svelte component. The results of that will need to be looped through. Until next time…

Resource

MDN - Using data attributes

MDN - Destructuring assignment

Sitepoint - JavaScript Truthy and Falsy

MDN - Conditional (ternary) operator