Displaying data (an array of objects) using Alpine JS in a table
This is from a perspective for those who haven't yet dived into JavaScript using frameworks / libraries like React, Angular and Vue and template engines. All three are good in their own ways - except that when using it for a single page or 3 page site, it'll consume 100+ MB in development mode and each time you need to deploy, you need to build - even after making a single character change after which the build or dist folder then needs to be re-uploaded in it's entirety to the server.
(Bootstrap 5.0.1 is used for styling - no JavaScript utilities included.)
Let's say we want to display name and description in a table and dynamically add (name, description) pairs to the table using a form.
- Load AlpineJS :
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
- Load some initial dummy content for the table :
let content = [
{ name: 'Test 1', description: 'Test Description 1' },
{ name: 'Test 2', description: 'Test Description 2' },
{ name: 'Test 3', description: 'Test Description 3' },
{ name: 'Test 4', description: 'Test Description 4' },
{ name: 'Test 5', description: 'Test Description 5' },
];
- Create the form without the
<form>
tag:
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" id="name" name="name" class="form-control" minlength="1" placeholder="Name" />
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<input type="text" id="description" name="description" class="form-control" minlength="1" placeholder="Description" />
</div>
<div class="mb-3">
<button type="submit" x-on:click="pushData(data);" class="btn btn-primary">Add</button>
</div>
We're not really posting the form's content to the server - hence no <form>
tag.
- In order to loop over the data (contents of the object) and display as a table we need to make use of Alpine's
<template>
tag in conjunction with thex-for
attribute.
<template x-for="(d, i) in data">
...
...
...
</template>
i is the index while d is the single object of data : data[i].
- We need a key for uniqueness of a row. Hence
:key="Date.now() + Math.floor(Math.random() * 1000000)"
We can access the data's single item as d.name
or data[i].name
Now, we'll use display: table
, display: table-row
and display: table-cell
to display the form data as a table. The reason why I am not using a <table>
itself is because using DIVs is easier.
<template x-for="(d, i) in data" :key="Date.now() + Math.floor(Math.random() * 1000000)">
<div style="display: table-row">
<div style="display: table-cell" x-text="i + 1"></div>
<div style="display: table-cell" x-text="d.name"></div>
<div style="display: table-cell" x-text="data[i].description"></div>
</div>
</template>
x-on:click="pushData(data);"
is used to call a function when the click event it triggered.Because we need to bind the data to the template, we set the data in a container div :
<div x-data="{ data: content }">
Full Code :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Div Form</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
<script>
let content = [
{ name: 'Test 1', description: 'Test Description 1' },
{ name: 'Test 2', description: 'Test Description 2' },
{ name: 'Test 3', description: 'Test Description 3' },
{ name: 'Test 4', description: 'Test Description 4' },
{ name: 'Test 5', description: 'Test Description 5' },
];
document.addEventListener("DOMContentLoaded", () =>
{
});
function pushData(data)
{
data.push({
name: document.getElementById('name').value,
description: document.getElementById('description').value
});
}
</script>
</head>
<body>
<div class="container mt-5">
<div x-data="{ data: content }">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" id="name" name="name" class="form-control" minlength="1" placeholder="Name" />
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<input type="text" id="description" name="description" class="form-control" minlength="1" placeholder="Description" />
</div>
<div class="mb-3">
<button type="submit" x-on:click="pushData(data);" class="btn btn-primary">Add</button>
</div>
<hr/>
<div class="table" style="display: table">
<template x-for="(d, i) in data" :key="Date.now() + Math.floor(Math.random() * 1000000)">
<div style="display: table-row">
<div style="display: table-cell" x-text="i + 1"></div>
<div style="display: table-cell" x-text="d.name"></div>
<div style="display: table-cell" x-text="data[i].description"></div>
</div>
</template>
</div>
</div>
</div>
</body>
</html>
anjanesh.s3.amazonaws.com/div-form.html
#alpine - Alpine Day is coming on June 10th : alpineday.com