13 KiB
DataTable Component Documentation
Overview
A feature-rich data table component built with PrimeVue's DataTable. This component provides advanced functionality including server-side pagination, sorting, manual filtering with apply buttons, row selection, page data caching, and customizable column types with persistent state management.
Basic Usage
<template>
<DataTable
:columns="tableColumns"
:data="tableData"
table-name="my-table"
@row-click="handleRowClick"
/>
</template>
<script setup>
import { ref } from "vue";
import DataTable from "./components/common/DataTable.vue";
const tableColumns = ref([
{
fieldName: "name",
label: "Name",
sortable: true,
filterable: true,
},
{
fieldName: "status",
label: "Status",
type: "status",
sortable: true,
filterable: true,
},
]);
const tableData = ref([
{ id: 1, name: "John Doe", status: "completed" },
{ id: 2, name: "Jane Smith", status: "in progress" },
]);
const handleRowClick = (event) => {
console.log("Row clicked:", event.data);
};
</script>
Props
columns (Array) - Required
- Description: Array of column configuration objects that define the table structure
- Type:
Array<Object> - Required:
true
data (Array) - Required
- Description: Array of data objects to display in the table
- Type:
Array<Object> - Required:
true
tableName (String) - Required
- Description: Unique identifier for the table, used for persistent filter state management
- Type:
String - Required:
true
totalRecords (Number)
- Description: Total number of records available on the server (for lazy loading)
- Type:
Number - Default:
0
onLazyLoad (Function)
- Description: Custom pagination event handler for server-side data loading
- Type:
Function - Default:
null
filters (Object)
- Description: Initial filter configuration object (used for non-lazy tables)
- Type:
Object - Default:
{ global: { value: null, matchMode: FilterMatchMode.CONTAINS } }
Server-Side Pagination & Lazy Loading
When lazy is set to true, the DataTable operates in server-side mode with the following features:
Automatic Caching
- Page Data Caching: Previously loaded pages are cached to prevent unnecessary API calls
- Cache Duration: 5 minutes default expiration time
- Cache Size: Maximum 50 pages per table with automatic cleanup
- Smart Cache Keys: Based on page, sorting, and filter combinations
Manual Filter Controls
- Apply Button: Filters are applied manually via button click to prevent excessive API calls
- Clear Button: Quick reset of all active filters
- Enter Key Support: Apply filters by pressing Enter in any filter field
- Visual Feedback: Shows active filters and pending changes
Quick Page Navigation
- Page Dropdown: Jump directly to any page number
- Page Info Display: Shows current record range and totals
- Persistent State: Page selection survives component re-mounts
Column Configuration
Each column object in the columns array supports the following properties:
Basic Properties
fieldName(String, required) - The field name in the data objectlabel(String, required) - Display label for the column headersortable(Boolean, default:false) - Enables sorting for this columnfilterable(Boolean, default:false) - Enables row-level filtering for this column
Column Types
type(String) - Defines special rendering behavior for the column
Available Types:
'status' Type
Renders values as colored tags/badges:
{
fieldName: 'status',
label: 'Status',
type: 'status',
sortable: true,
filterable: true
}
Status Colors:
'completed'→ Success (green)'in progress'→ Warning (yellow/orange)'not started'→ Danger (red)- Other values → Info (blue)
'button' Type
Renders values as clickable buttons:
{
fieldName: 'action',
label: 'Action',
type: 'button'
}
Events
rowClick
- Description: Emitted when a button-type column is clicked
- Payload: PrimeVue slot properties object containing row data
- Usage:
@row-click="handleRowClick"
lazy-load
- Description: Emitted when lazy loading is triggered (pagination, sorting, filtering)
- Payload: Event object with page, sorting, and filter information
- Usage:
@lazy-load="handleLazyLoad"
page-change
- Description: Emitted when page changes
- Payload: PrimeVue page event object
sort-change
- Description: Emitted when sorting changes
- Payload: PrimeVue sort event object
filter-change
- Description: Emitted when filters are applied
- Payload: PrimeVue filter event object
const handleRowClick = (slotProps) => {
console.log("Clicked row data:", slotProps.data);
console.log("Row index:", slotProps.index);
};
const handleLazyLoad = async (event) => {
// event contains: page, rows, sortField, sortOrder, filters
console.log("Lazy load event:", event);
// Load data from API based on event parameters
const result = await Api.getData({
page: event.page,
pageSize: event.rows,
sortField: event.sortField,
sortOrder: event.sortOrder,
filters: event.filters,
});
// Update component data
tableData.value = result.data;
totalRecords.value = result.totalRecords;
};
Features
Pagination
- Rows per page options: 5, 10, 20, 50
- Default rows per page: 10
- Built-in pagination controls
Sorting
- Multiple column sorting support
- Removable sort - click to remove sort from a column
- Sort indicators in column headers
Filtering
- Manual filter application with Apply/Clear buttons
- Text-based search for filterable columns
- Persistent filter state across component re-renders and page navigation
- Visual filter feedback showing active filters and pending changes
- Enter key support for quick filter application
Selection
- Multiple row selection with checkboxes
- Meta key selection (Ctrl/Cmd + click for individual selection)
- Unique row identification using
dataKey="id"
Scrolling
- Vertical scrolling with fixed height (70vh)
- Horizontal scrolling for wide tables
- Fixed headers during scroll
State Management
- Persistent filters using Pinia store (
useFiltersStore) - Automatic filter initialization on component mount
- Cross-component filter synchronization
Usage Examples
Server-Side Paginated Table (Recommended for Large Datasets)
<script setup>
import { ref } from "vue";
import DataTable from "./components/common/DataTable.vue";
import Api from "./api.js";
const columns = [
{ fieldName: "id", label: "ID", sortable: true },
{ fieldName: "name", label: "Name", sortable: true, filterable: true },
{ fieldName: "email", label: "Email", filterable: true },
];
const tableData = ref([]);
const totalRecords = ref(0);
const isLoading = ref(false);
const handleLazyLoad = async (event) => {
try {
isLoading.value = true;
// Convert PrimeVue event to API parameters
const params = {
page: event.page,
pageSize: event.rows,
sortField: event.sortField,
sortOrder: event.sortOrder,
};
// Convert filters
const filters = {};
if (event.filters) {
Object.keys(event.filters).forEach((key) => {
if (event.filters[key]?.value) {
filters[key] = event.filters[key];
}
});
}
// API call with caching support
const result = await Api.getPaginatedData(params, filters);
tableData.value = result.data;
totalRecords.value = result.totalRecords;
} catch (error) {
console.error("Error loading data:", error);
tableData.value = [];
totalRecords.value = 0;
} finally {
isLoading.value = false;
}
};
</script>
<template>
<DataTable
:data="tableData"
:columns="columns"
tableName="myTable"
:lazy="true"
:totalRecords="totalRecords"
:loading="isLoading"
:onLazyLoad="handleLazyLoad"
@lazy-load="handleLazyLoad"
/>
</template>
Basic Client-Side Table
<script setup>
const columns = [
{ fieldName: "id", label: "ID", sortable: true },
{ fieldName: "name", label: "Name", sortable: true, filterable: true },
{ fieldName: "email", label: "Email", filterable: true },
];
const data = [
{ id: 1, name: "John Doe", email: "john@example.com" },
{ id: 2, name: "Jane Smith", email: "jane@example.com" },
];
</script>
<template>
<DataTable :data="data" :columns="columns" tableName="basicTable" />
</template>
Status Table
<script setup>
const columns = [
{ fieldName: "task", label: "Task", sortable: true, filterable: true },
{
fieldName: "status",
label: "Status",
type: "status",
sortable: true,
filterable: true,
},
{ fieldName: "assignee", label: "Assignee", filterable: true },
];
const data = [
{ id: 1, task: "Setup project", status: "completed", assignee: "John" },
{ id: 2, task: "Write tests", status: "in progress", assignee: "Jane" },
{ id: 3, task: "Deploy app", status: "not started", assignee: "Bob" },
];
</script>
<template>
<DataTable :columns="columns" :data="data" table-name="tasks-table" />
</template>
Interactive Table with Buttons
<script setup>
const columns = [
{ fieldName: "name", label: "Name", sortable: true, filterable: true },
{ fieldName: "status", label: "Status", type: "status", sortable: true },
{ fieldName: "action", label: "Action", type: "button" },
];
const data = [
{ id: 1, name: "Project A", status: "completed", action: "View Details" },
{ id: 2, name: "Project B", status: "in progress", action: "Edit" },
];
const handleRowClick = (slotProps) => {
const { data, index } = slotProps;
console.log(`Action clicked for ${data.name} at row ${index}`);
// Handle the action (navigate, open modal, etc.)
};
</script>
<template>
<DataTable
:columns="columns"
:data="data"
table-name="projects-table"
@row-click="handleRowClick"
/>
</template>
Custom Filters
<script setup>
import { FilterMatchMode } from "@primevue/core";
const customFilters = {
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
name: { value: "John", matchMode: FilterMatchMode.STARTS_WITH },
};
</script>
<template>
<DataTable
:columns="columns"
:data="data"
:filters="customFilters"
table-name="filtered-table"
/>
</template>
Store Integration
The component integrates with a Pinia store (useFiltersStore) for persistent filter state:
Store Methods Used
initializeTableFilters(tableName, columns)- Initialize filters for a tablegetTableFilters(tableName)- Get current filters for a tableupdateTableFilter(tableName, fieldName, value, matchMode)- Update a specific filter
Filter Persistence
- Filters are automatically saved when changed
- Filters persist across component re-mounts
- Each table maintains separate filter state based on
tableName
Styling
The component uses PrimeVue's default DataTable styling with:
- Scrollable layout with fixed 70vh height
- Responsive design that adapts to container width
- Consistent spacing and typography
- Accessible color schemes for status badges
Performance Considerations
Large Datasets
- Virtual scrolling is not implemented - consider for datasets > 1000 rows
- Client-side pagination may impact performance with very large datasets
- Debounced filtering helps with real-time search performance
Memory Management
- Filter state persistence may accumulate over time
- Consider implementing filter cleanup for unused tables
- Component re-rendering is optimized through computed properties
Best Practices
- Use unique
tableNamefor each table instance to avoid filter conflicts - Define clear column labels for better user experience
- Enable sorting and filtering on searchable/comparable columns
- Use appropriate column types (
status,button) for better UX - Handle
rowClickevents for interactive functionality - Consider data structure - ensure
idfield exists for selection - Test with various data sizes to ensure performance
- Use consistent status values for proper badge coloring
Accessibility
The component includes:
- Keyboard navigation support via PrimeVue
- Screen reader compatibility with proper ARIA labels
- High contrast status badges for visibility
- Focus management for interactive elements
- Semantic HTML structure for assistive technologies
Browser Support
Compatible with all modern browsers that support:
- Vue 3 Composition API
- ES6+ features
- CSS Grid and Flexbox
- PrimeVue components
Dependencies
- Vue 3 with Composition API
- PrimeVue DataTable, Column, Tag, Button, InputText components
- @primevue/core for FilterMatchMode
- Pinia store for state management (
useFiltersStore)