Displaying data (an array of objects) using Alpine JS in a table

Displaying data (an array of objects) using Alpine JS in a table

alpine-1.png

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.

  1. Load AlpineJS :

<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>

  1. 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' },
];
  1. 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.

  1. 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 the x-for attribute.
<template x-for="(d, i) in data">
...
...
...
</template>

i is the index while d is the single object of data : data[i].

  1. 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>
  1. x-on:click="pushData(data);" is used to call a function when the click event it triggered.

  2. 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