505 lines
13 KiB
Markdown
505 lines
13 KiB
Markdown
# 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
|
|
|
|
```vue
|
|
<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 object
|
|
- **`label`** (String, required) - Display label for the column header
|
|
- **`sortable`** (Boolean, default: `false`) - Enables sorting for this column
|
|
- **`filterable`** (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:
|
|
|
|
```javascript
|
|
{
|
|
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:
|
|
|
|
```javascript
|
|
{
|
|
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
|
|
|
|
```javascript
|
|
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)
|
|
|
|
```vue
|
|
<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
|
|
|
|
```vue
|
|
<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
|
|
|
|
```vue
|
|
<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
|
|
|
|
```vue
|
|
<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
|
|
|
|
```vue
|
|
<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 table
|
|
- `getTableFilters(tableName)` - Get current filters for a table
|
|
- `updateTableFilter(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
|
|
|
|
1. **Use unique `tableName`** for each table instance to avoid filter conflicts
|
|
2. **Define clear column labels** for better user experience
|
|
3. **Enable sorting and filtering** on searchable/comparable columns
|
|
4. **Use appropriate column types** (`status`, `button`) for better UX
|
|
5. **Handle `rowClick` events** for interactive functionality
|
|
6. **Consider data structure** - ensure `id` field exists for selection
|
|
7. **Test with various data sizes** to ensure performance
|
|
8. **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`)
|