Learn how to create a API-backed Zelda BOTW monster gallery web component in <40 lines (Modulo.js)

michaelpb

michaelb

Posted on September 2, 2024

Learn how to create a API-backed Zelda BOTW monster gallery web component in <40 lines (Modulo.js)

Modulo tutorials are back!

Hey all! I'm back with Modulo tutorials after a summer hiatus. I've got a bunch more tutorials in the works -- so stay tuned. That said, if you have any particular ideas for my next topic, be sure to let me know in the comments!

My last tutorial was a super quick and fun "HTML-only, no-JS" tutorial on API-driven Pokémon Dance Party component in less than 30 lines of HTML Web Component code. Some of my previous tutorials were a bit more serious, such as this more advanced tutorial on managing private and public state. If that sounds a little dry, then you're in luck, because today's tutorial is another fun one, and about yet another beloved video game... Zelda: Breath of the Wild!

Of course, as always, the techniques learned in this tutorial are applicable to any API, so keep on reading to learn more about an API-driven gallery!

How to use the Hyrule Compendium API

This tutorial is 100% thanks to the wonderful Aarav Borthakur's free, MIT-licensed, and generously hosted Hyrule Compendium API, which is a fun, fan-maintained database and API for retrieval of Zelda: Breath of the Wild franchise information and media. We will be using the "Monsters" endpoint, available here: https://botw-compendium.herokuapp.com/api/v3/compendium/category/monsters

Screenshot

A screenshot of a gallery of Zelda monster images, with a description of

Try it out now, in less than 30 seconds: 🚀🚀🚀 Wanna skip ahead? Scroll to the end and copy the 39 lines of HTML code into any local HTML file, and then open it in your browser. Modulo has no dependencies and even runs embedded in local HTML files, so it's really that easy!


Start with the data

Let's start with just 6 lines of code, with a StaticData and a Template to display it:

<Template>
   <pre>API DATA: {{ staticdata|json:2 }}</pre>
</Template>
<StaticData
  -src="https://botw-compendium.herokuapp.com/api/v3/compendium/category/monsters"
></StaticData>
Enter fullscreen mode Exit fullscreen mode

In this snippet, we have a very simple, one-line <Template> that dumps the staticdata.data property of the returned Hyrule Compendium API. We apply the |json:2 filter to display it in a more readable format. The StaticData supports JSON (among other formats) out of the box, you just give it the URL to the API and you can then start using the data. Is StaticData confusing? Try this tutorial on integrating the GitHub API, or the play around with the interactive examples in the "StaticData" section of the tutorial of the Modulo.js tutorial.

Try running that snippet. See the resulting data? We'll need to loop through that with a for loop.

Creating an image gallery

Now that we can see that an attribute .data contains an Array of Objects, let's loop through it and generate a gallery:

<Template>
    {% for monster in staticdata.data %}
        <img src="{{ monster.image }}" style="width: 200px;" />
    {% endfor %}
</Template>
Enter fullscreen mode Exit fullscreen mode

This will generate many img tags, each with a src= assigned to the "image" properties of the Objects in the original JSON Array, and the {% for %} template-tag is the syntax to duplicate a bit of HTML for every item in the array (not to mention each index, e.g. a number counting up from 0). For further practice, the for-loop has lots of interactive examples in part 4 of the Modulo.js tutorial.

Creating State and Script

The next most important thing to do is create a new Script tag, which we can use to write a simple, one-line JavaScript function:

<State
    selected:=null
></State>
<Script>
    function select(payload) {
        state.selected = payload;
    }
</Script>
Enter fullscreen mode Exit fullscreen mode

This is a core technique for scripting while using Modulo: Create functions that let you modify state using JavaScript. In this case, it does a very simple operation: "Save this monster for later". More precisely, it assigns the state variable "selected" to the given payload. This way, the state variable "selected" becomes a sort of "stash" for whatever monster was just picked from the API.

Attaching the click event

Now, let's add another piece of the puzzle: Attaching the click event. See below:

<img @click:=script.select payload:="{{ monster|json }}" />
Enter fullscreen mode Exit fullscreen mode

This was done with the event attachment syntax (@click:=, in this
case), and a payload attribute, that lets us pass along the monster we are choosing by clicking this image. Events and Script tags can be a confusing topics if you are new to JavaScript (and even if you aren't!), so peruse the examples on this page for more examples of using Script component parts and attaching events.

Attaching the click event

Finally, let's conditionally render the monster info when a monster is selected:

{% if state.selected %}
    <h1>{{ state.selected.name|capfirst }}</h1>
    <p><img src="{{ state.selected.image }}" /></p>
    <p>{{ state.selected.description }}</p>
{% else %}
    <h1>Welcome to Hyrule Monster Guide!</h1>
    <p><em>&larr; Select a monster to learn more</em></p>
{% endif %}
Enter fullscreen mode Exit fullscreen mode

This will initially show the "Welcome" message (since state.selected begins as null). Then, as soon as someone clicks on a monster image, the state.selected variable will no longer be null, and
instead the contents will displayed formatted with h1 and p tags, with some tweaks applied (|capfirst makes the first letter capital).

<x-MonsterGuide> - Embeddable snippet

Combining it all, we then wrap everything in a display: grid to make the side-by-side layout, and a overflow: auto to the left div the scrollbar. Finally, we can add a few final CSS tweaks to the second div (padding, margin, and a linear-gradient), and we get the following results that can be embedded anywhere:

<!DOCTYPE html>
<template Modulo>
    <Component name="MonsterGuide">
        <Template>
            <main style="display: grid; grid-template-columns: 2fr 1fr">
                <div style="overflow: auto; height: 95vh;">
                    {% for monster in staticdata.data %}
                        <img src="{{ monster.image }}"
                            @click:=script.select payload:="{{ monster|json }}"
                            style="width: 200px;" />
                    {% endfor %}
                </div>
                <div style="padding: 10px; margin: 10px; background: linear-gradient(to bottom, lightyellow, goldenrod);">
                    {% if state.selected %}
                        <h1>{{ state.selected.name|capfirst }}</h1>
                        <p><img src="{{ state.selected.image }}" /></p>
                        <p>{{ state.selected.description }}</p>
                    {% else %}
                        <h1>Welcome to Hyrule Monster Guide!</h1>
                        <p><em>&larr; Select a monster to learn more</em></p>
                    {% endif %}
                </div>
            </main>
        </Template>
        <State
            selected:=null
        ></State>
        <StaticData
            -src="https://botw-compendium.herokuapp.com/api/v3/compendium/category/monsters"
        ></StaticData>
        <Script>
            function select(payload) {
                state.selected = payload;
            }
        </Script>
    </Component>
</template>
<script src="https://unpkg.com/mdu.js"></script>
<x-MonsterGuide></x-MonsterGuide>
Enter fullscreen mode Exit fullscreen mode

I hope you enjoyed this tutorial, if so, follow for more like this!

💖 💪 🙅 🚩
michaelpb
michaelb

Posted on September 2, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related