Learn how to create a API-backed Zelda BOTW monster gallery web component in <40 lines (Modulo.js)
michaelb
Posted on September 2, 2024
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
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>
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>
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>
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 }}" />
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>← Select a monster to learn more</em></p>
{% endif %}
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>← 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>
I hope you enjoyed this tutorial, if so, follow for more like this!
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
November 6, 2024