Data Table
Data tables display sets of data across rows and columns. They organize information in a way that’s easy to scan so that users can look for patterns and insights.
HTML
<div x-data="table">
<div class="d-flex justify-content-between mb-3">
<div>
<input type="text" class="form-control" placeholder="Search..." x-model="search">
</div>
</div>
<table class="table">
<thead>
<tr>
<template x-for="column in columns" :key="column.key">
<th scope="col" :class="sortColumn === column.key ? 'active' : ''"
@click="sort(column.key)">
<span x-text="column.name"></span>
<span x-show="sortColumn === column.key"
:class="sortDirection === 'asc' ? 'fa fa-arrow-up' : 'fa fa-arrow-down'"></span>
</th>
</template>
</tr>
</thead>
<tbody>
<template x-for="row in filteredAndSortedData()" :key="row.id">
<tr>
<td x-text="row.id"></td>
<td x-text="row.firstName"></td>
<td x-text="row.lastName"></td>
<td x-text="row.age"></td>
<td x-text="row.birthDate"></td>
<td x-text="row.email"></td>
</tr>
</template>
</tbody>
</table>
<div class="d-flex justify-content-between align-items-center mt-3">
<div>
<select class="form-select" x-model="limit" @change="changeLimit($event.target.value)">
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
</div>
<span>Page <span x-text="page"></span> of <span x-text="totalPages()"></span></span>
<div>
<button class="btn btn-secondary" :disabled="page === 1"
@click="page--">Previous</button>
<button class="btn btn-secondary" :disabled="page === totalPages()"
@click="page++">Next</button>
</div>
</div>
</div>
JS
<script>
document.addEventListener("alpine:init", () => {
Alpine.data("table", () => ({
tableData: [],
columns: [
{ name: "ID", key: "id" },
{ name: "Firstname", key: "firstName" },
{ name: "Lastname", key: "lastName" },
{ name: "Age", key: "age" },
{ name: "Start date", key: "birthDate" },
{ name: "Email", key: "email" }
],
search: "",
sortColumn: "id",
sortDirection: "asc",
page: 1,
limit: 5, // Number of rows per page
init() {
this.fetchData();
},
fetchData() {
fetch('https://dummyjson.com/users')
.then(res => res.json())
.then(data => {
this.tableData = data.users;
});
},
sort(column) {
if (this.sortColumn === column) {
this.sortDirection = this.sortDirection === "asc" ? "desc" : "asc";
} else {
this.sortColumn = column;
this.sortDirection = "asc";
}
},
changeLimit(limit) {
this.limit = limit;
this.page = 1;
},
filteredAndSortedData() {
let data = this.tableData;
// Search Filter
if (this.search) {
data = data.filter(row =>
Object.values(row).some(value =>
String(value).toLowerCase().includes(this.search.toLowerCase())
)
);
}
// Sorting
data.sort((a, b) => {
let valA = a[this.sortColumn];
let valB = b[this.sortColumn];
if (typeof valA === "string") valA = valA.toLowerCase();
if (typeof valB === "string") valB = valB.toLowerCase();
if (this.sortDirection === "asc") {
return valA > valB ? 1 : -1;
} else {
return valA < valB ? 1 : -1;
}
});
// Pagination
const start = (this.page - 1) * this.limit;
const end = start + this.limit;
return data.slice(start, end);
},
totalPages() {
return Math.ceil(this.tableData.length / this.limit);
},
}));
});
</script>