Table API Example - Server-side Pagination & Filtering

Introduction

This example demonstrates how to use the Table Manager with a PHP REST API that supports server-side pagination, searching and filtering.

Key Features:

  • Server-side Pagination: Reduces client load by paginating on the server
  • Server-side Search: Search runs on the server and supports large datasets
  • Server-side Filtering: Filtering performed server-side
  • Server-side Sorting: Sorting performed server-side
  • Real-time Updates: Real-time updates from the API
  • Performance: Improved performance for large datasets

Live Demo

This table loads data from a PHP REST API with server-side pagination, search and filters.

ID Name Position Department Email Status Salary Join Date

HTML Code

Example HTML markup for creating an API-backed table

<table class="table border fullwidth" data-table="api-employees" data-source="api/employees.php" data-page-size="10" data-search-columns="name,email,position,department" data-allow-row-modification="false" data-show-checkbox="true"> <thead> <tr> <th data-field="id" data-sort="id">ID</th> <th data-field="name" data-sort="name">Name</th> <th data-field="position" data-sort="position" data-filter="true" data-type="select" data-show-all="true" data-all-value="" data-label="Position" data-format="lookup">Position</th> <th data-field="department" data-sort="department" data-filter="true" data-type="select" data-show-all="true" data-all-value="" data-label="Department" data-format="lookup">Department</th> <th data-field="email" data-sort="email">Email</th> <th data-field="status" data-sort="status" data-filter="true" data-type="select" data-show-all="true" data-all-value="" data-label="Status" data-format="lookup">Status</th> <th data-field="salary" data-sort="salary" data-format="currency">Salary</th> <th data-field="join_date" data-sort="join_date">Join Date</th> </tr> </thead> <tbody></tbody> </table>

Data Attributes (explained):

Example JavaScript for handling table events emitted by the TableManager

Attribute Description Example
data-table Table identifier used to reference the instance api-employees
data-source URL of the API endpoint api/employees.php
data-page-size Items per page 10
data-search-columns name,email,position,department
events Register lightweight listeners using EventManager.on('table:render', handler) and EventManager.on('table:selectionChange', handler) -
data-format Display format lookup, currency

PHP API Endpoint

API endpoint that supports pagination, searching, and filtering

<?php header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); // Receive parameters from the query string $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; $pageSize = isset($_GET['pageSize']) ? (int)$_GET['pageSize'] : 10; $search = isset($_GET['search']) ? trim($_GET['search']) : ''; $sort = isset($_GET['sort']) ? $_GET['sort'] : ''; $order = isset($_GET['order']) ? $_GET['order'] : 'asc'; // Initialize example event listeners on DOM ready // Filters $department = isset($_GET['department']) ? $_GET['department'] : ''; $position = isset($_GET['position']) ? $_GET['position'] : ''; $status = isset($_GET['status']) ? $_GET['status'] : ''; // Minimal example listeners for the demo (use modern TableManager events) EventManager.on('table:render', function(payload) { if (payload.tableId === 'api-employees') { console.info('api-employees rendered', payload.data || []); } }); // Selection changes are emitted as table:selectionChange EventManager.on('table:selectionChange', function(payload) { if (payload.tableId === 'api-employees') { console.info('selection changed', payload.selectedRows || []); } }); 8 => 'HR Staff' ]; $statuses = [ 0 => 'Working', 1 => 'On Leave', 2 => 'Resigned', 3 => 'Probation' ]; // Employee data (example) $employees = [ [ 'id' => 1, 'name' => 'Somchai Jaidee', 'position' => 0, 'department' => 0, 'email' => 'somchai@example.com', 'status' => 0, 'salary' => 45000, 'join_date' => '2023-01-15' ], // ... other data ]; // Filter data $filteredData = $employees; // Search if (!empty($search)) { $filteredData = array_filter($filteredData, function ($employee) use ($search, $departments, $positions, $statuses) { $name = $employee['name']; $email = $employee['email']; $positionName = $positions[$employee['position']] ?? ''; $departmentName = $departments[$employee['department']] ?? ''; $statusName = $statuses[$employee['status']] ?? ''; return stripos($name, $search) !== false || stripos($email, $search) !== false || stripos($positionName, $search) !== false || stripos($departmentName, $search) !== false || stripos($statusName, $search) !== false; }); } // Filter by department if (!empty($department)) { $filteredData = array_filter($filteredData, function ($employee) use ($department) { return $employee['department'] == $department; }); } // Filter by position if (!empty($position)) { $filteredData = array_filter($filteredData, function ($employee) use ($position) { return $employee['position'] == $position; }); } // Filter by status if (!empty($status)) { $filteredData = array_filter($filteredData, function ($employee) use ($status) { return $employee['status'] == $status; }); } // Sorting if (!empty($sort)) { usort($filteredData, function ($a, $b) use ($sort, $order) { $aVal = $a[$sort]; $bVal = $b[$sort]; if ($order === 'desc') { return $bVal <=> $aVal; } return $aVal <=> $bVal; }); } // Calculate pagination $totalRecords = count($filteredData); $totalPages = ceil($totalRecords / $pageSize); $offset = ($page - 1) * $pageSize; $paginatedData = array_slice($filteredData, $offset, $pageSize); // Prepare filter options as arrays (prefer array for stable ordering) $filterDepartments = []; $filterDepartments[] = ['value' => '', 'text' => 'All']; foreach ($departments as $k => $v) { $filterDepartments[] = ['value' => $k, 'text' => $v]; } $filterPositions = []; $filterPositions[] = ['value' => '', 'text' => 'All']; foreach ($positions as $k => $v) { $filterPositions[] = ['value' => $k, 'text' => $v]; } $filterStatuses = []; $filterStatuses[] = ['value' => '', 'text' => 'All']; foreach ($statuses as $k => $v) { $filterStatuses[] = ['value' => $k, 'text' => $v]; } // Build response (use array for filters to preserve ordering) $response = [ 'data' => array_values($paginatedData), 'meta' => [ 'total' => $totalRecords, 'page' => $page, 'pageSize' => $pageSize, 'totalPages' => $totalPages, 'hasNext' => $page < $totalPages, 'hasPrev' => $page > 1 ], 'filters' => [ 'department' => $filterDepartments, 'position' => $filterPositions, 'status' => $filterStatuses ] ]; echo json_encode($response, JSON_UNESCAPED_UNICODE); ?>

JavaScript Events

JavaScript examples for handling API table events

// Example: preferred event handlers for the API-backed table function setupApiTableEvents() { // Listen to render events to get the currently displayed page and meta EventManager.on('table:render', function(payload) { if (payload.tableId === 'api-employees') { exampleLog('API table rendered', { data: payload.data, total: payload.totalRecords, filtered: payload.filteredRecords }); } }); // Listen to selection changes EventManager.on('table:selectionChange', function(payload) { if (payload.tableId === 'api-employees') { exampleLog('Selection changed', payload.selectedRows); } }); } // Call on DOM ready document.addEventListener('DOMContentLoaded', function() { if (window.EventManager && typeof EventManager.on === 'function') { setupApiTableEvents(); } }); // Function to refresh API table data function refreshApiTable() { const tableId = 'api-employees'; const table = TableManager.state.tables.get(tableId); if (table) { TableManager.loadData(tableId, table.config.source); } } // Function to change page size function changePageSize(size) { const tableId = 'api-employees'; const table = TableManager.state.tables.get(tableId); if (table) { table.config.params.pageSize = size; table.config.params.page = 1; // reset to first page TableManager.loadData(tableId, table.config.source); } }

Features of the API Example

Server-side Pagination

Server-side pagination reduces client load and improves performance

Server-side Search

Server-side search supports large datasets and lookup field searches

Server-side Filtering

Server-side filtering supports multiple filter types

Server-side Sorting

Server-side sorting supports multi-field sorting

Real-time Updates

Real-time updates from the API

Performance

Improved performance for large datasets

API Parameters

Parameters supported by the API

Parameter Type Description Example
page integer Page number to request 1, 2, 3...
pageSize integer Items per page 10, 25, 50
search string Search term e.g. somchai
sort string Field to sort by name, salary, join_date
order string Sort direction asc, desc
department string Filter by department 0, 1, 2...
position string Filter by position 0, 1, 2...
status string Filter by status 0, 1, 2...

Response Format

API response format

{ "data": [ { "id": 1, "name": "Somchai Jaidee", "position": 0, "department": 0, "email": "somchai@example.com", "status": 0, "salary": 45000, "join_date": "2023-01-15" } ], "meta": { "total": 30, "page": 1, "pageSize": 10, "totalPages": 3, "hasNext": true, "hasPrev": false }, "filters": { "department": { "": "All", "0": "Development", "1": "Systems Analysis" }, "position": { "": "All", "0": "Programmer", "1": "Analyst" }, "status": { "": "All", "0": "Working", "1": "On Leave" } } }

Advanced Features

Advanced features for the API

1. Custom API Headers

// Set custom headers for API const tableConfig = { source: 'api/employees.php', headers: { 'Authorization': 'Bearer your-token-here', 'X-API-Key': 'your-api-key', 'Content-Type': 'application/json' } }; TableManager.init(document.querySelector('#myTable'), tableConfig);

2. API Response Transformation

// Function to transform API response function transformApiResponse(data) { return { data: data.employees.map(emp => ({ ...emp, fullName: `${emp.firstName} ${emp.lastName}`, salaryFormatted: new Intl.NumberFormat('th-TH', { style: 'currency', currency: 'THB' }).format(emp.salary) })), meta: data.pagination }; }

3. API Caching

// API cache system const apiCache = new Map(); function getCachedData(key) { const cached = apiCache.get(key); if (cached && Date.now() - cached.timestamp < 300000) { // 5 minutes return cached.data; } return null; } function setCachedData(key, data) { apiCache.set(key, { data: data, timestamp: Date.now() }); }

4. Real-time Updates

// Real-time updates system function setupRealTimeUpdates(tableId) { const eventSource = new EventSource('api/employees-stream.php'); eventSource.onmessage = function(event) { const data = JSON.parse(event.data); if (data.type === 'update') { // Update table data const table = TableManager.state.tables.get(tableId); if (table) { const existingData = table.data; const updatedData = existingData.map(item => item.id === data.employee.id ? data.employee : item ); TableManager.setData(tableId, updatedData); } } }; }