Searchable Data Table

Filter and sort tabular data.

Live Demo

Name Email Role Status

Source

HTML + JavaScript
<div data-component="data-table">
    <input type="search" placeholder="Search..." data-model="searchQuery">
    <table>
        <thead>
            <tr>
                <th data-action="sortBy" data-col="name">Name</th>
                <th data-action="sortBy" data-col="email">Email</th>
                <th data-action="sortBy" data-col="role">Role</th>
                <th data-action="sortBy" data-col="status">Status</th>
            </tr>
        </thead>
        <tbody data-list="filteredUsers">
            <template>
                <tr>
                    <td data-bind="name"></td>
                    <td data-bind="email"></td>
                    <td data-bind="role"></td>
                    <td data-bind="status"></td>
                </tr>
            </template>
        </tbody>
    </table>
    <p data-bind="resultCount"></p>
</div>

<script>
wildflower.component('data-table', {
    state: {
        searchQuery: '',
        sortCol: 'name',
        sortAsc: true,
        users: [
            { name: 'Alice Chen', email: 'alice@example.com', role: 'Admin', status: 'Active' },
            { name: 'Bob Martinez', email: 'bob@example.com', role: 'Editor', status: 'Active' },
            { name: 'Carol Johnson', email: 'carol@example.com', role: 'Viewer', status: 'Inactive' },
            { name: 'Dave Wilson', email: 'dave@example.com', role: 'Editor', status: 'Pending' },
            { name: 'Eve Park', email: 'eve@example.com', role: 'Admin', status: 'Active' }
        ]
    },
    computed: {
        filteredUsers() {
            const q = this.searchQuery.toLowerCase();
            const col = this.sortCol;
            const asc = this.sortAsc;
            return this.users
                .filter(u => !q || u.name.toLowerCase().includes(q) || u.email.toLowerCase().includes(q))
                .sort((a, b) => {
                    const cmp = a[col].localeCompare(b[col]);
                    return asc ? cmp : -cmp;
                });
        },
        resultCount() {
            return this.filteredUsers.length + ' of ' + this.users.length + ' users';
        }
    },
    sortBy(e) {
        const col = e.target.dataset.col;
        if (this.sortCol === col) {
            this.sortAsc = !this.sortAsc;
        } else {
            this.sortCol = col;
            this.sortAsc = true;
        }
    }
});
</script>

Key Points

  • data-model on the search input provides two-way binding; the list updates as you type
  • The filteredUsers computed does both filtering and sorting in one pass
  • Clicking a column header toggles sort direction via data-col attribute
  • The result count is a computed that reads from the filtered list