Practical Alpine.js: x-data data access & data fetching/AJAX examples
Alpine.js “A rugged, minimal framework for composing JavaScript behavior in your markup.” is a great, lightweight replacement for interactive jQuery or Vanilla JS sprinkles due to its small size (~7kb min-gzipped) & the fact it can be included from a CDN with a script
tag.
Caleb Porzio, Alpine.js’ creator, likens it to “Tailwind for JavaScript”. One of the great things about Tailwind is that the examples provided are very much copy-pasteable since the logic lives in the HTML/markup instead of in a CSS file off somewhere else.
A lot of Caleb’s Alpine.js examples in the README (no official documentation site at the time of writing), use pure HTML markup, functionality is declared inline of the attributes that use it.
This allows the Alpine.js examples to be very concise, but coming from a Vue.js/React background, I like to wrap my functionality in an x-data
function that’s not declared inline, it’s in a script
tag later on in the page. This also means that shared JavaScript/Alpine.js components can be extracted to a .js
file and included using <script src="./path/to/fn.js"></script>
.
This post is about how to access, fetch and set data in Alpine.js components using a setup where JavaScript is in script tags or a separate file, not inline in the markup.
Table of Contents
Access data set returned by your x-data
function in component method
Let’s say we’ve got an Alpine.js component that shows a greeting when clicked. The greeting is set by the function bound with x-data
so that we can later add a UI to edit it.
<div x-data="greet()">
<button @click="showMessage()">Greet Me</button>
<div>Message: <span x-text="messageDisplay"></span></div>
</div>
We can define greet
as follows, with an empty messageDisplay
, a message
set to “Hello Alpine.js” and an empty (for now) showMessage()
function.
<script>
function greet() {
return {
message: 'Hello Alpine.js',
messageDisplay: '',
showMessage() {
// implement me
}
}
}
</script>
A very cool thing that Alpine.js does is that the this
context inside of any method/function in the object returned from the x-data
expression will be bound to the component instance, which has all the data.
This all means that we have access to both this.message
and this.messageDisplay
from inside of showMessage
.
<script>
function greet() {
return {
message: 'Hello Alpine.js',
messageDisplay: '',
showMessage() {
console.log(this.message)
}
}
}
</script>
You will see “Hello Alpine.js” in the console when you click the “Greet Me” button.
So functions defined on the object returned from our x-data
function have access to the other properties on that object using this.propertyName
syntax. The same holds true for other methods.
Next we’ll look at how we can set data from within methods.
Set data on the Alpine.js instance from a method defined on x-data
The following template, tries to render messageDisplay
in a span using the x-text
directive.
<div x-data="greet()">
<button @click="showMessage()">Greet Me</button>
<div>Message: <span x-text="messageDisplay"></span></div>
</div>
Current state & expected state before clicking the “Greet Me” button is as follows, the button renders and “Message: " is displayed but nothing in the span
(after “Message: “).
In order to do this, we’ll go and set the value of messageDisplay
to the value of message
.
<script>
function greet() {
return {
message: "Hello Alpine.js",
messageDisplay: "",
showMessage() {
this.messageDisplay = this.message;
}
};
}
</script>
Now when you click “Greet Me” the “Message: Hello Alpine.js” will display.
See the following recording:
You can see the full example in action at alpinejs.codewithhugo.com/x-data-access
Fetch data and display it with Alpine.js
For this next section we’ll look at fetching some data from the Pokémon API.
We’ll be building a simple search box for Pokemon that displays an image, the name and some abilities.
To start with, we can build our search input which we’ll 2-way bind using v-model
. When the input changes, so does this.pokemonSearch
.
<div x-data="pokeSearch()">
<input
type="text"
name="pokemonSearch"
x-model="pokemonSearch"
/>
</div>
<script>
function pokeSearch() {
return {
pokemonSearch: '',
}
}
</script>
Next step is to add button which will kick off the search API call to pokeapi. We’ll call that data fetching function fetchPokemon
. We’ll also disable the button while we’re loading data (which will be stored in the isLoading
field).
<div x-data="pokeSearch()">
<!-- rest of HTML -->
<button
type="submit"
@click="fetchPokemon()"
:disabled="isLoading"
>
Search
</button>
</div>
Next step is to implement the fetchPokemon
function, which will call to the https://pokeapi.co/api/v2/pokemon/
endpoint with pokemonSearch
, and will store the output into this.pokemon
which we’ll initialise to null
. It also toggles isLoading
while the fetch is happening.
<script>
function pokeSearch() {
return {
// other default properties
isLoading: false,
pokemon: null,
fetchPokemon() {
this.isLoading = true;
fetch(`https://pokeapi.co/api/v2/pokemon/${this.pokemonSearch}`)
.then(res => res.json())
.then(data => {
this.isLoading = false;
this.pokemon = data;
});
}
}
}
</script>
Finally it’s time to display the pokemon we’ll use x-if
to conditionally display a pokemon based on whether it’s loaded or not. pokemon.sprite.front_default
will be our main image and we’ll loop through pokemon.abilities
using x-for
.
<div x-data="pokeSearch()">
<!-- Rest of the HTML -->
<template x-if="pokemon">
<img
:src="pokemon.sprites.front_default"
:alt="pokemon.name"
/>
<h3 x-text="pokemon.name"></h3>
<template
x-for="abilityObj in pokemon.abilities"
:key="abilityObj.ability.url"
>
<span x-text="abilityObj.ability.name"></span>
</template>
</template>
</div>
We have a Pokemon Search component in Alpine.js, with extra sprinkles of TailwindCSS styling this is what it looks like in action, try it at https://alpinejs.codewithhugo.com/fetch-data.
Find more examples like these on Alpine.js Playground - “A set of ready to use Alpine.js examples with TailwindCSS”.
Find more Alpine.js posts on Code with Hugo - Alpine.js.
Interested in Alpine.js?
Subscribe to Alpine.js Weekly. A free, once–weekly email roundup of Alpine.js news and articles