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?
Subscribe to Alpine.js Weekly. A free, once–weekly email roundup of Alpine.js news and articles