NoundryTable - Advanced Data Grid

A powerful, server-side paginated data grid inspired by svelte-tablecn. All parameters are sent to the API for filtering, sorting, and pagination.

User Management

Full-featured table with search, sorting, pagination, selection, and export.

Users

Manage your team members

View Code
<noundry-data-grid
    api-url="/api/users"
    title="Users"
    description="Manage your team members"
    page-size="10"
    page-size-options="5,10,25,50"
    default-sort-column="name"
    default-sort-direction="asc"
    enable-search="true"
    enable-sorting="true"
    enable-selection="true"
    enable-multi-select="true"
    enable-column-toggle="true"
    enable-export="true"
    enable-refresh="true"
    hoverable="true"
    striped="false"
    row-id-property="id">

    <noundry-data-grid-col key="id" label="ID" sortable="true" width="80px" align="center" />
    <noundry-data-grid-col key="name" label="Name" sortable="true" />
    <noundry-data-grid-col key="email" label="Email" sortable="true" href="mailto:{email}" />
    <noundry-data-grid-col key="department" label="Department" sortable="true" />
    <noundry-data-grid-col key="status" label="Status" type="badge"
        badge-variants='{"active":"success","inactive":"error","pending":"warning"}' />
    <noundry-data-grid-col key="salary" label="Salary" type="currency" sortable="true" align="right" />
    <noundry-data-grid-col key="hireDate" label="Hire Date" type="date" sortable="true" />
    <noundry-data-grid-col key="active" label="Active" type="boolean" align="center" />
</noundry-data-grid>

Product Inventory

Compact table with striped rows and different column configurations.

Products

View Code
<noundry-data-grid
    api-url="/api/products"
    title="Products"
    page-size="10"
    enable-search="true"
    enable-sorting="true"
    enable-pagination="true"
    striped="true"
    row-height="compact"
    row-id-property="id">

    <noundry-data-grid-col key="sku" label="SKU" sortable="true" width="120px" />
    <noundry-data-grid-col key="name" label="Product Name" sortable="true" />
    <noundry-data-grid-col key="category" label="Category" sortable="true" />
    <noundry-data-grid-col key="price" label="Price" type="currency" sortable="true" align="right" />
    <noundry-data-grid-col key="stock" label="Stock" type="number" sortable="true" align="right" />
    <noundry-data-grid-col key="status" label="Status" type="badge"
        badge-variants='{"in_stock":"success","low_stock":"warning","out_of_stock":"error"}' />
    <noundry-data-grid-col key="createdAt" label="Created" type="date" sortable="true" />
</noundry-data-grid>

API Integration

The NoundryDataGrid component sends the following query parameters to your API:

GET /api/users?page=1&pageSize=10&sortColumn=name&sortDirection=asc&search=john

Expected Response Format

{
    "data": [
        { "id": 1, "name": "John Doe", "email": "john@example.com", ... }
    ],
    "total": 150
}

Example C# API Controller

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetUsers(
        int page = 1,
        int pageSize = 10,
        string? sortColumn = null,
        string? sortDirection = "asc",
        string? search = null)
    {
        var query = _dbContext.Users.AsQueryable();

        // Apply search filter
        if (!string.IsNullOrEmpty(search))
        {
            query = query.Where(u =>
                u.Name.Contains(search) ||
                u.Email.Contains(search));
        }

        // Apply sorting
        query = sortColumn switch
        {
            "name" => sortDirection == "desc"
                ? query.OrderByDescending(u => u.Name)
                : query.OrderBy(u => u.Name),
            "email" => sortDirection == "desc"
                ? query.OrderByDescending(u => u.Email)
                : query.OrderBy(u => u.Email),
            _ => query.OrderBy(u => u.Id)
        };

        var total = query.Count();
        var data = query
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToList();

        return Ok(new { data, total });
    }
}

Column Types

text

Default. Displays plain text.

number

Formats numbers with locale separators.

currency

Formats as currency (default USD).

date

Formats as localized date.

datetime

Formats as localized date and time.

boolean

Shows checkmark or X icon.

badge

Colored badge with variant mapping.

Accessing Selected IDs (Client-Side)

Use Alpine.js to access the selected row IDs from JavaScript:

// Get the data grid element by ID
const grid = document.getElementById('users-grid');

// Access the Alpine.js component data
const data = Alpine.$data(grid);

// Get selected IDs as an array
const selectedIds = Array.from(data.selectedRows);
console.log('Selected IDs:', selectedIds);

// Get full row data for selected items
const selectedRows = data.data.filter(row => data.isSelected(row));
console.log('Selected Rows:', selectedRows);

// Clear all selections
data.clearSelection();

// Check selection count
const count = data.selectedRows.size;
console.log(`${count} items selected`);

Example: Bulk Actions with Selected IDs

// Function to delete selected users
async function deleteSelectedUsers() {
    const grid = document.getElementById('users-grid');
    const data = Alpine.$data(grid);
    const selectedIds = Array.from(data.selectedRows);

    if (selectedIds.length === 0) {
        alert('No users selected');
        return;
    }

    if (!confirm(`Delete ${selectedIds.length} user(s)?`)) {
        return;
    }

    try {
        const response = await fetch('/api/users/bulk-delete', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ ids: selectedIds })
        });

        if (response.ok) {
            // Refresh the grid after deletion
            data.refresh();
            data.clearSelection();
        }
    } catch (error) {
        console.error('Delete failed:', error);
    }
}
@section Scripts { }