+ Smarter Tables, Less Hassle!
+ SmartTables.js is a powerful, feature-rich JavaScript plugin designed to enhance HTML tables with advanced
+ functionality such as sorting, searching, pagination, responsive behavior, and data export capabilities.
+ It is built with modern web development practices in mind, offering a flexible and extensible API for developers
+ to customize and extend its functionality.
+
+ This plugin is ideal for developers who need to manage large datasets in a user-friendly way, providing a seamless
+ experience for end-users while maintaining a high level of customization and control.
+
+
+ Pro Tip: SmartTables works with your existing HTML tables and automatically adapts to different screen sizes by hiding less important columns on smaller screens.
+
+
+ Installation
+ SmartTables.js is now available as an ES6 module. You can include it in your project in several ways:
+
+ 1. Using ES6 Modules (Recommended)
+
+
// Import the SmartTables class
+import { SmartTables } from './optional/smartTables/smartTables.bundle.js';
+
+// Initialize with empty options object (required)
+const myTable = new SmartTables('tableId', {});
+
+// Or with custom options
+const myTable = new SmartTables('tableId', {
+ perPage: 15,
+ search: true,
+ sort: true,
+ pagination: true,
+ export: true
+});
+
+
+
+
Important:
+
+ - You must pass an empty options object (
{}) as the second parameter when initializing SmartTables, even if you don't need any custom options.
+ - This is required because the constructor expects an options object to properly initialize default values.
+ - If you don't pass the options object, you'll get an error:
Cannot read properties of undefined (reading 'responsive').
+
+
+
+ 2. Using NPM (for modern build systems)
+
+
// Install via npm
+npm install smarttables
+
+// Import in your JavaScript/TypeScript file
+import { SmartTables } from 'smarttables';
+
+// Use in your component
+const myTable = new SmartTables('tableId', {
+ data: {
+ type: 'json',
+ source: myData,
+ columns: [
+ { data: 'id', title: 'ID' },
+ { data: 'name', title: 'Name' },
+ { data: 'position', title: 'Position' }
+ ]
+ }
+});
+
+
+ 3. Using with TypeScript
+
+
import { SmartTables, SmartTablesOptions } from 'smarttables';
+
+interface CustomOptions extends SmartTablesOptions {
+ customFeature?: boolean;
+}
+
+const options: CustomOptions = {
+ perPage: 10,
+ search: true,
+ customFeature: true
+};
+
+const myTable = new SmartTables('tableId', options);
+
+
+ HTML Structure
+
+
<table id="myTable" class="table">
+ <thead>
+ <tr>
+ <th data-priority="1">ID</th>
+ <th data-priority="2">Name</th>
+ <th data-priority="3">Position</th>
+ <th data-priority="4">Office</th>
+ <th data-priority="5">Age</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>John Doe</td>
+ <td>Developer</td>
+ <td>New York</td>
+ <td>32</td>
+ </tr>
+ <!-- More rows... -->
+ </tbody>
+</table>
+
+
+
+
Important:
+
+ - Use the
data-priority attribute on table headers to control which columns are hidden first when the table becomes too narrow for the viewport.
+ - When using ES6 modules, make sure your server is configured to serve JavaScript files with the correct MIME type (
application/javascript).
+ - For older browsers, you'll need to use a bundler like Webpack, Rollup, or Parcel to transpile the ES6 code.
+
+
+
+ Configuration Options
+ SmartTables.js provides extensive configuration options to customize its behavior. All options are optional and will fall back to sensible defaults.
+
+
+
// Basic configuration
+const table = new SmartTables('myTable', {
+ perPage: 10,
+ search: true,
+ sort: true
+});
+
+// Advanced configuration with TypeScript
+interface CustomOptions extends SmartTablesOptions {
+ customFeature?: boolean;
+}
+
+const options: CustomOptions = {
+ perPage: 25,
+ search: true,
+ sort: true,
+ pagination: true,
+ export: true,
+ loading: {
+ enabled: true,
+ duration: 500,
+ minDuration: 300
+ },
+ responsive: {
+ enabled: true,
+ breakpoint: 992,
+ columnPriorities: {
+ 0: 1, // ID column - highest priority
+ 1: 2, // Name column - second highest priority
+ 2: 3, // Position
+ 3: 4, // Office
+ 4: 5 // Age - lowest priority
+ }
+ },
+ debug: false,
+ fuzzyMatch: {
+ threshold: 0.6,
+ minMatchLength: 2,
+ multiWordThreshold: 0.5,
+ maxDistance: 3
+ },
+ customFeature: true
+};
+
+const table = new SmartTables('myTable', options);
+
+
+
+
+
+
+ | Option |
+ Default Value |
+ Description |
+ Type |
+
+
+
+
+
+ perPage |
+ 10 |
+ Number of rows to display per page in pagination. |
+ number |
+
+
+ search |
+ true |
+ Enables or disables the search functionality for the table. |
+ boolean |
+
+
+ sort |
+ true |
+ Enables or disables sorting functionality for table columns. |
+ boolean |
+
+
+ pagination |
+ true |
+ Enables or disables pagination controls for the table. |
+ boolean |
+
+
+ export |
+ true |
+ Enables or disables export functionality (e.g., Excel, CSV, Copy). |
+ boolean |
+
+
+
+
+ loading.enabled |
+ true |
+ Enables or disables the loading spinner/indicator during data loading or processing. |
+ boolean |
+
+
+ loading.duration |
+ 0 |
+ Duration (in milliseconds) of an artificial delay for the loading indicator. |
+ number |
+
+
+ loading.minDuration |
+ 300 |
+ Minimum duration (in milliseconds) to display the loading indicator. |
+ number |
+
+
+
+
+ responsive.enabled |
+ true |
+ Enables or disables responsive behavior (e.g., hiding columns on small screens). |
+ boolean |
+
+
+ responsive.breakpoint |
+ 768 |
+ Breakpoint (in pixels) at which responsive behavior starts. |
+ number |
+
+
+ responsive.columnPriorities |
+ {} |
+ Object mapping column indices to their priority (1 being highest). |
+ Object<number, number> |
+
+
+ responsive.details.type |
+ 'column' |
+ Type of detail display for hidden columns. |
+ string |
+
+
+ responsive.details.target |
+ 0 |
+ Target column index for the detail control. |
+ number |
+
+
+
+
+ debug |
+ false |
+ Enables or disables debug logging for development and troubleshooting. |
+ boolean |
+
+
+
+
+ fuzzyMatch.threshold |
+ 0.7 |
+ Threshold for fuzzy matching (0.0–1.0). |
+ number |
+
+
+ fuzzyMatch.minMatchLength |
+ 2 |
+ Minimum number of characters required for fuzzy matching. |
+ number |
+
+
+ fuzzyMatch.multiWordThreshold |
+ 0.5 |
+ Threshold for multi-word search matches. |
+ number |
+
+
+ fuzzyMatch.maxDistance |
+ 2 |
+ Maximum Levenshtein distance allowed for typo tolerance. |
+ number |
+
+
+
+
+ classes.wrapper |
+ 'st-wrapper' |
+ Class for the wrapper div containing the table. |
+ string |
+
+
+ classes.table |
+ 'st-table table table-striped table-hover' |
+ Class for the table element. |
+ string |
+
+
+ classes.toolbar |
+ 'st-toolbar d-flex justify-content-between mb-3' |
+ Class for the toolbar containing controls. |
+ string |
+
+
+ classes.search |
+ 'st-search form-control' |
+ Class for the search input field. |
+ string |
+
+
+ classes.pagination |
+ 'st-pagination pagination justify-content-center' |
+ Class for the pagination controls. |
+ string |
+
+
+ classes.export |
+ 'st-export btn-group' |
+ Class for the export button group. |
+ string |
+
+
+
+
+ data.type |
+ null |
+ Type of data source ('json', 'csv', 'ajax', or null for DOM-based tables). |
+ string|null |
+
+
+ data.source |
+ null |
+ Source of the data (URL, string, or object depending on type). |
+ string|null|Object |
+
+
+ data.columns |
+ [] |
+ Array of column definitions. |
+ Array |
+
+
+ data.processing |
+ false |
+ Enables or disables a processing indicator during data operations. |
+ boolean |
+
+
+ data.serverSide |
+ false |
+ Enables or disables server-side processing for large datasets. |
+ boolean |
+
+
+ data.method |
+ 'GET' |
+ HTTP method for AJAX requests. |
+ string |
+
+
+ data.headers |
+ {} |
+ Custom headers for AJAX requests. |
+ Object |
+
+
+ data.params |
+ {} |
+ Additional parameters for AJAX requests. |
+ Object |
+
+
+ data.parser |
+ null |
+ Custom parser function for transforming raw data. |
+ Function|null |
+
+
+ data.prefetch |
+ true |
+ Enables prefetching of the next page data for smoother pagination. |
+ boolean |
+
+
+ data.cacheDuration |
+ 300000 |
+ Duration in milliseconds to keep cached data (default: 5 minutes). |
+ number |
+
+
+ data.withCredentials |
+ false |
+ Whether to include credentials in cross-domain requests. |
+ boolean |
+
+
+ data.url |
+ null |
+ The URL to fetch data from (aliased with source for compatibility). |
+ string|null |
+
+
+
+
+ hooks |
+ {} |
+ Object containing various lifecycle and event hooks. |
+ Object |
+
+
+
+
+ plugins |
+ [] |
+ Array of plugin objects to extend functionality. |
+ Array |
+
+
+
+
+
+
+ Pro Tip: You can extend the default options by creating a custom interface that extends SmartTablesOptions when using TypeScript. This provides better type safety and autocompletion.
+
+
+ Data Loading Methods
+ SmartTables supports various data loading methods:
+
+
// Load from JSON data
+var myTable = new SmartTables('tableId', {
+ data: {
+ type: 'json',
+ source: [
+ { id: 1, name: 'John Doe', position: 'Developer', office: 'New York', age: 32 },
+ { id: 2, name: 'Jane Smith', position: 'Designer', office: 'London', age: 28 }
+ ],
+ columns: [
+ { data: 'id', title: 'ID' },
+ { data: 'name', title: 'Name' },
+ { data: 'position', title: 'Position' },
+ { data: 'office', title: 'Office' },
+ { data: 'age', title: 'Age' }
+ ]
+ }
+});
+
+// Load from AJAX
+var myTable = new SmartTables('tableId', {
+ data: {
+ type: 'ajax',
+ source: 'api/users',
+ method: 'GET',
+ headers: { 'Authorization': 'Bearer token123' },
+ params: { limit: 100 }
+ }
+});
+
+// Load from CSV
+var myTable = new SmartTables('tableId', {
+ data: {
+ type: 'csv',
+ source: 'data/employees.csv'
+ }
+});
+
+
+ Data Import Functionality
+ SmartTables provides built-in data import capabilities that allow users to import data from various file formats directly into the table. This feature can be enabled by setting the import: true option.
+
+
+
// Initialize table with import enabled
+var myTable = new SmartTables('tableId', {
+ import: true, // Enable import functionality
+ data: {
+ type: 'json',
+ source: initialData,
+ columns: [
+ { data: 'id', title: 'ID' },
+ { data: 'name', title: 'Name' },
+ { data: 'position', title: 'Position' }
+ ]
+ }
+});
+
+
+ Supported Import Formats
+
+ - CSV Files: Import data from comma-separated value files
+ - JSON Files: Import data from JSON format files
+ - Excel Files: Import data from Excel spreadsheets (.xlsx)
+ - Text Files: Import data from plain text files with custom delimiters
+
+
+ Import Options
+
+
var myTable = new SmartTables('tableId', {
+ import: {
+ enabled: true,
+ formats: ['csv', 'json', 'xlsx'], // Specify allowed formats
+ maxFileSize: 5 * 1024 * 1024, // 5MB max file size
+ onImportStart: function() {
+ console.log('Import started');
+ },
+ onImportComplete: function(data) {
+ console.log('Import completed', data);
+ },
+ onImportError: function(error) {
+ console.error('Import failed', error);
+ }
+ }
+});
+
+
+ Example Usage
+
+
<!-- HTML Structure -->
+<table id="myTable" class="table">
+ <thead>
+ <tr>
+ <th data-priority="1">ID</th>
+ <th data-priority="2">Name</th>
+ <th data-priority="3">Position</th>
+ </tr>
+ </thead>
+ <tbody>
+ <!-- Data will be populated by SmartTables -->
+ </tbody>
+</table>
+
+<!-- JavaScript -->
+import { SmartTables } from 'smarttables';
+
+const table = new SmartTables('myTable', {
+ import: true,
+ data: {
+ type: 'json',
+ source: [], // Start with empty data
+ columns: [
+ { data: 'id', title: 'ID' },
+ { data: 'name', title: 'Name' },
+ { data: 'position', title: 'Position' }
+ ]
+ }
+});
+
+
+
+ Pro Tip: When importing data, SmartTables automatically detects the column structure and maps the imported data to the appropriate columns. You can also provide custom column mappings for more control over the import process.
+
+
+
+
Important:
+
+ - Make sure your server is configured to handle file uploads if you're using the file import feature
+ - Consider implementing file size limits and format validation on both client and server side
+ - For large files, consider implementing chunked uploads or progress indicators
+ - Always validate imported data before processing to prevent security issues
+
+
+
+ Server-side Processing
+ SmartTables provides robust server-side processing for handling large datasets efficiently. By enabling data.serverSide: true, data filtering, sorting, and pagination are offloaded to the server, ideal for datasets with thousands or millions of records. The server processes requests and returns only the necessary data for display, ensuring fast performance.
+
+ Server-side Configuration
+ Configure SmartTables to fetch data from a server endpoint using AJAX with server-side processing enabled:
+
+
import { SmartTables } from 'smarttables';
+const serverTable = new SmartTables('serverTable', {
+ data: {
+ type: 'ajax',
+ source: '/api/employees',
+ serverSide: true,
+ method: 'GET',
+ prefetch: true,
+ cacheDuration: 300000, // 5 minutes cache duration
+ columns: [
+ { data: 'id', title: 'ID' },
+ { data: 'name', title: 'Name' },
+ { data: 'department', title: 'Department' },
+ { data: 'age', title: 'Age' },
+ { data: 'salary', title: 'Salary' },
+ { data: 'bonus', title: 'Bonus' }
+ ]
+ }
+});
+
+
+
+ Server Response Format
+ The server must return data in the following JSON format, compatible with SmartTables' expectations:
+
+
{
+ "data": [
+ { "id": 1, "name": "John Doe", "department": "Finance", "age": 32, "salary": 85000, "bonus": 5000 },
+ { "id": 2, "name": "Jane Smith", "department": "HR", "age": 28, "salary": 65000, "bonus": 3000 }
+ // ... more records
+ ],
+ "total": 1000 // Total records after filtering
+}
+
+
+
+
+ Important: Unlike some table libraries, SmartTables expects total instead of recordsTotal or recordsFiltered. Ensure your server returns the correct field to avoid pagination issues.
+
+
+ Request Parameters
+ SmartTables sends the following query parameters in GET requests to the server:
+
+
+
+
+ | Parameter |
+ Description |
+ Example |
+
+
+
+
+ page |
+ Current page number (1-based) |
+ 2 |
+
+
+ perPage |
+ Number of records per page |
+ 10 |
+
+
+ search |
+ Global search value |
+ Finance |
+
+
+ sortColumn |
+ Name of the column being sorted |
+ department |
+
+
+ sortDirection |
+ Direction of sort |
+ asc or desc |
+
+
+
+
+
+
+ Pro Tip: Use the data.params option to customize parameter names if your server expects different ones (e.g., limit instead of perPage):
+
+ data: {
+ type: 'ajax',
+ source: '/api/employees',
+ serverSide: true,
+ params: {
+ limit: function() { return this.perPage; },
+ offset: function() { return (this.currentPage - 1) * this.perPage; },
+ query: 'search'
+ }
+}
+
+
+ Advanced Caching for Server-side Data
+ SmartTables enhances performance with client-side caching and prefetching:
+
+ - Cache Keys: Unique keys are generated based on
page, search, sortColumn, and sortDirection.
+ - Prefetching: Automatically loads the next page when
prefetch: true, reducing wait times.
+ - Cache Invalidation: Clears cache on parameter changes (e.g., new search term).
+ - Cache Duration: Configurable via
cacheDuration (default: 5 minutes).
+
+
+
+
const serverTable = new SmartTables('serverTable', {
+ data: {
+ type: 'ajax',
+ source: '/api/employees',
+ serverSide: true,
+ prefetch: true,
+ cacheDuration: 600000, // 10 minutes
+ withCredentials: true,
+ headers: { 'X-API-Key': 'your-api-key' }
+ },
+ debug: true // Log cache operations
+});
+
+
+ Implementing a Server-side Endpoint
+ Below are example implementations in multiple languages to demonstrate flexibility. Your server should handle pagination, filtering, and sorting based on the request parameters and return the expected response format.
+
+
+
+
+
+
+
+
This example uses ASP.NET Core with Entity Framework Core for database access, implementing server-side processing with caching.
+
+
using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Caching.Memory;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+public class Employee
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Department { get; set; }
+ public int Age { get; set; }
+ public decimal Salary { get; set; }
+ public decimal Bonus { get; set; }
+}
+
+public class PaginatedResponse<T>
+{
+ public T[] Data { get; set; }
+ public int Total { get; set; }
+}
+
+[ApiController]
+[Route("api/[controller]")]
+public class EmployeesController : ControllerBase
+{
+ private readonly AppDbContext _context;
+ private readonly IMemoryCache _cache;
+
+ public EmployeesController(AppDbContext context, IMemoryCache cache)
+ {
+ _context = context;
+ _cache = cache;
+ }
+
+ [HttpGet]
+ public async Task<IActionResult> GetEmployees(
+ [FromQuery] int page = 1,
+ [FromQuery] int perPage = 10,
+ [FromQuery] string search = "",
+ [FromQuery] string sortColumn = "",
+ [FromQuery] string sortDirection = "asc")
+ {
+ try
+ {
+ // Generate cache key
+ string cacheKey = $"employees_{page}_{perPage}_{search}_{sortColumn}_{sortDirection}";
+
+ // Try to get from cache
+ if (!_cache.TryGetValue(cacheKey, out PaginatedResponse<Employee> response))
+ {
+ // Base query
+ var query = _context.Employees.AsQueryable();
+
+ // Apply search
+ if (!string.IsNullOrEmpty(search))
+ {
+ query = query.Where(e =>
+ e.Name.Contains(search, StringComparison.OrdinalIgnoreCase) ||
+ e.Department.Contains(search, StringComparison.OrdinalIgnoreCase) ||
+ e.Age.ToString().Contains(search) ||
+ e.Salary.ToString().Contains(search) ||
+ e.Bonus.ToString().Contains(search));
+ }
+
+ // Get total count
+ int total = await query.CountAsync();
+
+ // Apply sorting
+ if (!string.IsNullOrEmpty(sortColumn))
+ {
+ query = sortDirection.ToLower() == "asc"
+ ? query.OrderBy(e => EF.Property<object>(e, sortColumn))
+ : query.OrderByDescending(e => EF.Property<object>(e, sortColumn));
+ }
+
+ // Apply pagination
+ query = query.Skip((page - 1) * perPage).Take(perPage);
+
+ // Execute query
+ var data = await query.ToArrayAsync();
+
+ response = new PaginatedResponse<Employee>
+ {
+ Data = data,
+ Total = total
+ };
+
+ // Cache the result
+ _cache.Set(cacheKey, response, TimeSpan.FromMinutes(5));
+ }
+
+ return Ok(response);
+ }
+ catch (Exception ex)
+ {
+ return StatusCode(500, new { data = Array.Empty<Employee>(), total = 0, error = "Internal server error" });
+ }
+ }
+}
+
+
+
+
ASP.NET Core Tips:
+
+ - Use
IMemoryCache or IDistributedCache for caching responses.
+ - Validate
sortColumn to prevent SQL injection when using dynamic sorting.
+ - Consider using dependency injection for database context and logging.
+
+
+
+
+
+
+
+
+
+
+
+
A simple Express.js implementation with in-memory data, adaptable to any database.
+
+
const express = require('express');
+const cors = require('cors');
+const app = express();
+
+app.use(cors());
+app.use(express.json());
+
+// Mock data (replace with database)
+const employees = [
+ { id: 1, name: 'John Doe', department: 'Engineering', age: 32, salary: 85000, bonus: 5000 },
+ { id: 2, name: 'Jane Smith', department: 'Marketing', age: 28, salary: 65000, bonus: 3000 }
+ // ... more records
+];
+
+app.get('/api/employees', async (req, res) => {
+ try {
+ const { page = 1, perPage = 10, search = '', sortColumn = '', sortDirection = 'asc' } = req.query;
+ const pageNum = parseInt(page);
+ const perPageNum = parseInt(perPage);
+
+ // Filter data
+ let filteredData = employees;
+ if (search) {
+ const searchLower = search.toLowerCase();
+ filteredData = employees.filter(item =>
+ Object.values(item).some(val =>
+ String(val).toLowerCase().includes(searchLower)
+ )
+ );
+ }
+
+ // Get total
+ const total = filteredData.length;
+
+ // Sort data
+ if (sortColumn) {
+ filteredData.sort((a, b) => {
+ const aVal = a[sortColumn];
+ const bVal = b[sortColumn];
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
+ return sortDirection === 'asc' ? aVal - bVal : bVal - aVal;
+ }
+ const aStr = String(aVal).toLowerCase();
+ const bStr = String(bVal).toLowerCase();
+ return sortDirection === 'asc' ? aStr.localeCompare(bStr) : bStr.localeCompare(aStr);
+ });
+ }
+
+ // Paginate
+ const offset = (pageNum - 1) * perPageNum;
+ const paginatedData = filteredData.slice(offset, offset + perPageNum);
+
+ res.json({
+ data: paginatedData,
+ total
+ });
+ } catch (error) {
+ console.error(error);
+ res.status(500).json({ data: [], total: 0, error: 'Internal server error' });
+ }
+});
+
+app.listen(3000, () => console.log('Server running on port 3000'));
+
+
+
+
Node.js Tips:
+
+ - Integrate with a database like MongoDB or PostgreSQL for production.
+ - Use a caching layer like Redis for better performance.
+ - Add middleware for authentication if needed.
+
+
+
+
+
+
+
+
+
+
+
+
A FastAPI implementation using SQLAlchemy for database operations.
+
+
from fastapi import FastAPI, Depends
+from sqlalchemy.orm import Session
+from sqlalchemy import or_, desc, asc
+from typing import Optional
+from pydantic import BaseModel
+import models
+from database import SessionLocal
+
+app = FastAPI()
+
+# Pydantic model for response
+class PaginatedResponse(BaseModel):
+ data: list
+ total: int
+
+def get_db():
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
+
+@app.get("/api/employees", response_model=PaginatedResponse)
+async def get_employees(
+ page: int = 1,
+ perPage: int = 10,
+ search: Optional[str] = None,
+ sortColumn: Optional[str] = None,
+ sortDirection: str = "asc",
+ db: Session = Depends(get_db)
+):
+ try:
+ # Base query
+ query = db.query(models.Employee)
+
+ # Apply search
+ if search:
+ query = query.filter(
+ or_(
+ models.Employee.name.ilike(f"%{search}%"),
+ models.Employee.department.ilike(f"%{search}%"),
+ models.Employee.age.cast(str).ilike(f"%{search}%"),
+ models.Employee.salary.cast(str).ilike(f"%{search}%"),
+ models.Employee.bonus.cast(str).ilike(f"%{search}%")
+ )
+ )
+
+ # Get total
+ total = query.count()
+
+ # Apply sorting
+ if sortColumn:
+ column = getattr(models.Employee, sortColumn, None)
+ if column:
+ query = query.order_by(asc(column) if sortDirection == "asc" else desc(column))
+
+ # Apply pagination
+ offset = (page - 1) * perPage
+ query = query.offset(offset).limit(perPage)
+
+ # Execute
+ data = query.all()
+
+ return {
+ "data": data,
+ "total": total
+ }
+ except Exception as e:
+ return {"data": [], "total": 0, "error": str(e)}
+
+
+
+
Python Tips:
+
+ - Use Pydantic models for response validation.
+ - Implement caching with libraries like
cachetools.
+ - Secure endpoints with FastAPI's security utilities.
+
+
+
+
+
+
+
+ Handling Pagination with Search
+ Ensure consistent total counts across requests to prevent pagination issues:
+
+
const table = new SmartTables('myTable', {
+ data: {
+ serverSide: true,
+ source: '/api/employees',
+ method: 'GET'
+ },
+ hooks: {
+ afterDataLoad: function(response) {
+ console.log('Total records:', response.total);
+ }
+ }
+});
+
+
+
+
+
Important Considerations:
+
+ - Calculate
total after applying filters but before pagination.
+ - Reset to page 1 when search changes to avoid invalid page requests.
+ - Validate
sortColumn to ensure it matches valid fields.
+ - Use logging to debug inconsistencies in counts or data.
+
+
+
+ Real-time Debugging
+ Enable debug: true to log AJAX requests, responses, and cache operations:
+
+ - Request parameters sent to the server.
+ - Cache hits, misses, and key generation.
+ - Prefetching status and errors.
+ - Total counts and pagination calculations.
+
+
+
+ Pro Tip: Combine client-side debug logs with server-side logging to troubleshoot issues. Check that total remains consistent across pages for the same search query.
+
+
+ Managing Records with SmartTables
+ SmartTables provides powerful functionality for managing records with full CRUD (Create, Read, Update, Delete) operations. This allows you to build interactive data management interfaces with minimal effort.
+
+ Basic Setup for Record Management
+
+
// Import the SmartTables module
+import { SmartTables } from './optional/smartTables/smartTables.bundle.js';
+
+document.addEventListener('DOMContentLoaded', () => {
+ // Initialize SmartTables with data management options
+ const clientTable = new SmartTables('clientTable', {
+ // Data configuration
+ data: {
+ type: 'json',
+ source: sampleData, // Your data array
+ idField: 'id', // Field that uniquely identifies each record
+ columns: [
+ { data: 'id', title: 'ID', editable: false },
+ { data: 'name', title: 'Name', required: true },
+ { data: 'email', title: 'Email', type: 'email', required: true },
+ { data: 'phone', title: 'Phone', type: 'tel' },
+ { data: 'active', title: 'Active', type: 'boolean' },
+ {
+ data: 'actions',
+ title: 'Actions',
+ sortable: false,
+ editable: false,
+ render: (data, row) =>
+ '<button type="button" class="btn btn-primary btn-xs edit-btn" data-id="' + row.id + '">Edit</button> ' +
+ '<button type="button" class="btn btn-danger btn-xs delete-btn" data-id="' + row.id + '">Del</button>'
+ }
+ ]
+ },
+ // Enable record management features
+ addRecord: true, // Shows an Add Record button
+ // Other options...
+ });
+
+ // Handle edit and delete button clicks with event delegation
+ clientTable.table.addEventListener('click', e => {
+ if (e.target.classList.contains('edit-btn')) {
+ const rowId = e.target.getAttribute('data-id');
+ clientTable.edit(rowId);
+ } else if (e.target.classList.contains('delete-btn')) {
+ const rowId = e.target.getAttribute('data-id');
+ clientTable.delete(rowId);
+ }
+ });
+});
+
+
+ Advanced Record Management with Hooks
+ SmartTables provides a comprehensive hooks system to customize the behavior of record management operations:
+
+
const table = new SmartTables('myTable', {
+ // ... other options
+ hooks: {
+ // Edit operation hooks
+ beforeEdit(rowId) {
+ // Called before edit mode is entered
+ console.log('Editing row: ' + rowId);
+ return true; // Return false to prevent editing
+ },
+ afterEdit(rowId, rowData, success) {
+ // Called after edit is completed
+ console.log('Edit completed: ' + success);
+ },
+ onEditModalCreated(modalHTML, rowId, rowData) {
+ // Customize the edit modal HTML
+ return modalHTML;
+ },
+ onEditModalBeforeShow(modalElement, rowId, rowData) {
+ // Access the modal DOM before it's shown
+ },
+ onEditDataCollected(updatedData, rowId, originalData) {
+ // Process and validate form data
+ return updatedData;
+ },
+ onEditSuccess(rowId, updatedRecord, submittedData) {
+ // Handle successful update
+ console.log('Record updated successfully');
+ },
+ onEditError(rowId, error, attemptedData) {
+ // Handle update errors
+ console.error('Update failed', error);
+ },
+
+ // Delete operation hooks
+ beforeDelete(rowId) {
+ // Called before deletion
+ return confirm('Are you sure you want to delete this record?');
+ },
+ afterDelete(rowId, data, success) {
+ // Called after deletion attempt
+ if (success) console.log('Deletion successful');
+ },
+ onDeleteModalCreated(modalHtml, rowId) {
+ // Customize deletion confirmation modal
+ return modalHtml;
+ },
+ onDeleteSuccess(rowId, deletedRecord) {
+ // Handle successful deletion
+ },
+
+ // Add record operation hooks
+ beforeAddRecord(initialData, options) {
+ // Called before add form is shown
+ return initialData; // Return modified initial data
+ },
+ afterAddRecord(newRecordData, success) {
+ // Called after add operation completes
+ if (success) console.log('Record added');
+ },
+ onAddModalCreated(modalHTML, initialData, options) {
+ // Customize add record modal
+ return modalHTML;
+ },
+ onAddModalBeforeShow(modalElement, initialData, options) {
+ // DOM access before modal shows
+ },
+ onAddDataCollected(newRecordData, options) {
+ // Process and validate new record data
+ return newRecordData;
+ },
+ onAddRecordSuccess(newRecord) {
+ // Handle successful addition
+ },
+ onAddRecordError(error, attemptedData) {
+ // Handle addition errors
+ },
+ onAddCancelled(options) {
+ // Called when add is cancelled
+ }
+ }
+});
+
+
+ Column Type Configuration
+ SmartTables supports various input types for different data fields:
+
+
+
+
+ | Type |
+ Description |
+ Additional Options |
+
+
+
+
+ text |
+ Default text input |
+ maxlength, pattern |
+
+
+ number |
+ Numeric input |
+ min, max, step |
+
+
+ email |
+ Email input with validation |
+ pattern |
+
+
+ tel |
+ Telephone input |
+ format, placeholder |
+
+
+ date |
+ Date picker |
+ min, max |
+
+
+ select |
+ Dropdown selection |
+ options (array) |
+
+
+ boolean |
+ Checkbox |
+ - |
+
+
+ password |
+ Password input |
+ minlength |
+
+
+
+
+
+
+
// Column configuration examples for different types
+columns: [
+ {
+ data: 'salary',
+ title: 'Salary',
+ type: 'number', // Numeric input
+ min: 0, // Minimum value
+ step: 500, // Increment by 500
+ render: data => '$' + data.toLocaleString() // Format with $ and commas
+ },
+ {
+ data: 'department',
+ title: 'Department',
+ type: 'select', // Dropdown select input
+ options: [ // Available options
+ 'Engineering',
+ 'Sales',
+ 'Marketing',
+ 'Human Resources'
+ ]
+ },
+ {
+ data: 'joinDate',
+ title: 'Join Date',
+ type: 'date', // Date input
+ render: data => new Date(data).toLocaleDateString() // Format date
+ },
+ {
+ data: 'active',
+ title: 'Active',
+ type: 'boolean', // Boolean/checkbox
+ render: data => data ?
+ '<span class="badge bg-success">Yes</span>' :
+ '<span class="badge bg-danger">No</span>'
+ }
+]
+
+
+ Visual Feedback for Row States
+ Implement visual feedback for different row states during CRUD operations:
+
+
// Track row states
+const rowStates = {
+ editing: null, // ID of the row being edited
+ saved: new Set(), // Set of recently saved row IDs
+ adding: false // Flag for adding operation
+};
+
+// Apply classes in hooks
+hooks: {
+ beforeEdit(rowId) {
+ rowStates.editing = rowId;
+ const rowElement = this.table.querySelector('tbody tr[data-id="' + rowId + '"]');
+ if (rowElement) {
+ rowElement.classList.add('editing');
+ }
+ return true;
+ },
+
+ afterEdit(rowId, rowData, success) {
+ rowStates.editing = null;
+ if (success) {
+ rowStates.saved.add(rowId);
+ setTimeout(() => {
+ rowStates.saved.delete(rowId);
+ const row = this.table.querySelector('tbody tr[data-id="' + rowId + '"]');
+ if (row) row.classList.remove('saved');
+ }, 3000);
+ }
+ this.draw();
+ }
+}
+
+
+
+ Pro Tip: When implementing CRUD operations, consider using a custom modal system for consistency across the application. SmartTables hooks make it easy to integrate with custom UI components.
+
+
+