Alpine.js `x-for` with objects: 4 ways to iterate/loop through JavaScript objects
One of the missing features of Alpine.js is the ability to iterate through objects with x-for
.
Alpine.js is heavily inspired by Vue.js but it’s designed to be lean and rugged instead of incrementally adoptable. Therefore x-for
only supports arrays/iterables.
The reason there’s no first-class support for iterating through objects with Alpine.js’ x-for
is that converting a JavaScript Object to an Array is reasonably easy in modern JavaScript (ES6+) environments using Object.keys
, Object.values
or even Object.entries
. This is the purpose of this post.
You can find all the examples at Alpine.js Playground - x-for with object.
Table of Contents
Concept: “normalised” data shape
In this example we’ll have some todos
stored as on our instance, the data itself is of the following shape:
const todos = {
1: {
id: 1,
text: 'todo-1'
},
2: {
id: 2,
text: 'todo-2'
}
}
Note that this is often referred to as the “normalised” form of the data, because each item (todo) can be looked up by id eg. for id = 1: todos[1]
. This is opposed to the array form in which lookups by id can be more difficult. For example we could store the same todos as:
const todos = [
{ id: 1, text: 'todo-1' },
{ id: 2, text: 'todo-2' }
]
In this case a lookup for the todo with id 1 will necessitate the use of the Array#find
method ie. todos.find(el => el.id === 1)
, which is more verbose and complex.
We’ve now had a look at the data shape we’ll be using, we can put it into an inline Alpine.js component’s x-data
:
<div
x-data="{
todos: {
1: {
id: 1,
text: 'todo-1'
},
2: {
id: 2,
text: 'todo-2'
}
}
}"
>
</div>
A basic feature of a todo app is the ability to display a list of todos. Next we’ll see how to display a list of normalised todos from state in Alpine.js using Object.keys
.
Iterate through object keys/ids with x-for and Object.keys
Assuming a normalised data shape of todos keyed by id, if we need to list out the ids (1, 2), which are also the keys of the todos object we can use x-for="id in Object.keys(todos)"
as per the following example.
<div
x-data="{
todos: {
1: { id: 1, text: 'todo-1' },
2: { id: 2, text: 'todo-2' }
}
}"
>
<!-- rest of template -->
<h3>Only need the keys/ids - use <code>Object.keys</code></h3>
<ul>
<template x-for="id in Object.keys(todos)" :key="id">
<li>id: <span x-text="id"></span></li>
</template>
</ul>
</div>
Which yields the following output:
You can see it in action at Alpine.js Playground - x-for with object - Only need the keys/ids - use Object.keys.
We’ve now seen how to iterate through an object’s keys using x-for
and Object.keys
.
Next we’ll see how to iterate through object key-values pairs with x-for
and Object.entries
.
Iterate through object key-value pairs with x-for and Object.entries
When both the id/key and the value are required, we can iterate through a JavaScript object with Alpine.js’ x-for
and Object.entries
using the following directive: x-for="[id, value] in Object.entries(todos)"
.
A full example populating select
options would look as follows:
<div
x-data="{
todos: {
1: { id: 1, text: 'todo-1' },
2: { id: 2, text: 'todo-2' }
}
}"
>
<!-- rest of template -->
<h3>Need keys/ids and values - use <code>Object.entries</code></h3>
<select>
<template
x-for="[id, value] in Object.entries(todos)"
:key="id"
>
<option :value="id" x-text="value.text"></option>
</template>
</select>
</div>
Note this is using Array destructuring, an alternative syntax for it that doesn’t required environment support for array destructuring is
x-for="entry in Object.entries(todos)"
and usingentry[0]
andentry[1]
to refer to theid
(key) andvalue
respectively.
Alternative syntax (without array destructuring):
<div
x-data="{
todos: {
1: { id: 1, text: 'todo-1' },
2: { id: 2, text: 'todo-2' }
}
}"
>
<!-- rest of template -->
<h3>Need keys/ids and values - use <code>Object.entries</code></h3>
<select>
<template
x-for="entry in Object.entries(todos)"
:key="entry[0]"
>
<option :value="entry[0]" x-text="entry[1].text"></option>
</template>
</select>
</div>
Both of these are functionally equivalent albeit with a terseness/support tradeoff and yield the following output:
You can see it in action at Alpine.js Playground - x-for with object - Need keys/ids and values - use Object.entries.
We’ve now seen how to use Object.entries
and x-for
to loop through an object when both the key (id) and vale is needed.
Next we’ll see an alternative way to achieve the same outcome using x-for
, Object.values
and object value lookup by id.
Iterate through object keys and look up corresponding object values with x-for and Object.keys
We’ve seen how to iterate through object keys using Object.keys
. What’s great about a normalised data shape (entities keyed by id), is that looking up a value is just todos[id]
. That means that we can achieve the same outcome as we did with Object.entries
using only Object.keys
.
We use x-for="id in Object.keys(todos)
and then get the text using todos[id].text
.
<div
x-data="{
todos: {
1: { id: 1, text: 'todo-1' },
2: { id: 2, text: 'todo-2' }
}
}"
>
<h3>Need keys/ids and values - use <code>Object.keys</code> + lookup</h3>
<select>
<template x-for="id in Object.keys(todos)" :key="id">
<option :value="id" x-text="todos[id].text"></option>
</template>
</select>
</div>
Which yields the following select with “todo-1” and “todo-2” options, where the value
will be recorded as 1
and 2
respectively.
You can see it in action at Alpine.js Playground - x-for with object - Need keys/ids and values - use Object.keys + lookup .
Nothing forces you to use the id
once you have it, we can actually achieve something similar to our Object.values
use case using Object.keys
+ a key lookup.
Here we x-for="id in Object.keys(todos)"
and get the todo text using todos[id].text
, not that the id isn’t displayed or used apart from to set the key
.
<div
x-data="{
todos: {
1: { id: 1, text: 'todo-1' },
2: { id: 2, text: 'todo-2' }
}
}"
>
<h3>Need values - use <code>Object.keys</code> + lookup</h3>
<ul>
<template x-for="id in Object.keys(todos)" :key="id">
<li x-text="todos[id].text"></li>
</template>
</ul>
</div>
This outputs the following list, which looks pretty much like the list we’ll create using Object.values
.
You can see it in action at Alpine.js Playground - x-for with object: Need values - use Object.keys + lookup.
We’ve now seen how to iterate through an object’s keys using x-for
and Object.keys
as well as the flexibility a normalised data shape provides us. We showed this by re-implementing the other types of object iteration using Object.keys
and key lookups.
Next we’ll look at iterating through object values using x-for
and Object.values
.
Iterate through object values with Object.values
For this guide to be complete, we’ll close off by looking at how to iterate through object values in Alpine.js using x-for
and Object.values
.
Object.values
, as its name suggests, turns an object into an array of its values.
We can therefore use x-for="todo in Object.values(todos)
and use the todo
value in our loop template.
<div
x-data="{
todos: {
1: { id: 1, text: 'todo-1' },
2: { id: 2, text: 'todo-2' }
}
}"
>
<h3>Need only values - use <code>Object.values</code></h3>
<ul>
<template x-for="todo in Object.values(todos)" :key="todo">
<li x-text="todo.text"></li>
</template>
</ul>
</div>
This yields a similar list as we saw in the previous section, with todo-1
and todo-2
.
You can see it in action at Alpine.js Playground - x-for with object - Need only values - use Object.values.
Photo by Clay Banks on Unsplash
Interested in Alpine.js?
Power up your debugging with the Alpine.js Devtools Extension for Chrome and Firefox. Trusted by over 15,000 developers (rated 4.5 ⭐️).