/ #alpinejs #javascript 

Practical Alpine.js: x-data data access & data fetching 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.

"Hello Alpine.js" in the console after clicking "Greet Me"

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: “).

"Greet me" rendering as well as "Message: " but no 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.

After clicking "Greet me", "Message: Hello Alpine.js" displays

See the following recording:

Full "Greet Me" click video

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.

"Charizard" Pokemon Search Box with Alpine.js and TailwindCSS

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.

unsplash-logoMarkus Spiske

Author

Hugo Di Francesco

Co-author of "Professional JavaScript" with Packt. He runs the Code with Hugo website helping over 100,000 developers every month and holds an MEng in Mathematical Computation from University College London (UCL). He has used JavaScript extensively to create scalable and performant platforms at companies such as Canon and Elsevier.

Interested in Alpine.js?

Subscribe to Alpine.js Weekly A free, once–weekly email roundup of Alpine.js news and articles