954 lines
25 KiB
Markdown
954 lines
25 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"
|
|
:tableActions="tableActions"
|
|
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 tableActions = ref([
|
|
{
|
|
label: "Add Item",
|
|
action: () => console.log("Add clicked"),
|
|
icon: "pi pi-plus",
|
|
style: "primary",
|
|
// Global action - always available
|
|
},
|
|
{
|
|
label: "View Details",
|
|
action: (rowData) => console.log("View:", rowData),
|
|
icon: "pi pi-eye",
|
|
style: "info",
|
|
requiresSelection: true,
|
|
// Single selection action - enabled when exactly one row selected
|
|
},
|
|
{
|
|
label: "Edit",
|
|
action: (rowData) => console.log("Edit:", rowData),
|
|
icon: "pi pi-pencil",
|
|
style: "secondary",
|
|
rowAction: true,
|
|
// Row action - appears in each row's actions column
|
|
},
|
|
]);
|
|
|
|
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 } }`
|
|
|
|
### `tableActions` (Array)
|
|
|
|
- **Description:** Array of action objects that define interactive buttons for the table. Actions can be global (always available), single-selection (enabled when exactly one row is selected), row-specific (displayed per row), or bulk (for multiple selected rows).
|
|
- **Type:** `Array<Object>`
|
|
- **Default:** `[]`
|
|
|
|
## 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'
|
|
}
|
|
```
|
|
|
|
## Table Actions Configuration
|
|
|
|
Table actions allow you to add interactive buttons to your DataTable. Actions can be either global (displayed above the table) or row-specific (displayed in an actions column).
|
|
|
|
### Action Object Properties
|
|
|
|
Each action object in the `tableActions` array supports the following properties:
|
|
|
|
#### Basic Properties
|
|
|
|
- **`label`** (String, required) - Display text for the button
|
|
- **`action`** (Function, required) - Function to execute when button is clicked
|
|
- **`icon`** (String, optional) - PrimeVue icon class (e.g., 'pi pi-plus')
|
|
- **`style`** (String, optional) - Button severity: 'primary', 'secondary', 'success', 'info', 'warning', 'danger'
|
|
- **`size`** (String, optional) - Button size: 'small', 'normal', 'large'
|
|
- **`requiresSelection`** (Boolean, default: false) - When true, action appears above table but is only enabled when exactly one row is selected
|
|
- **`requiresMultipleSelection`** (Boolean, default: false) - Determines if action is for bulk operations on selected rows
|
|
- **`rowAction`** (Boolean, default: false) - When true, action appears in each row's actions column
|
|
- **`layout`** (Object, optional) - Layout configuration for action positioning and styling
|
|
|
|
#### Layout Configuration
|
|
|
|
The `layout` property allows you to control where and how actions are displayed:
|
|
|
|
##### For Top-Level Actions (Global and Single Selection)
|
|
|
|
```javascript
|
|
layout: {
|
|
position: "left" | "center" | "right", // Where to position in action bar
|
|
variant: "filled" | "outlined" | "text" // Visual style variant
|
|
}
|
|
```
|
|
|
|
##### For Row Actions
|
|
|
|
```javascript
|
|
layout: {
|
|
priority: "primary" | "secondary" | "dropdown", // Display priority in row
|
|
variant: "outlined" | "text" | "compact" | "icon-only" // Visual style
|
|
}
|
|
```
|
|
|
|
##### For Bulk Actions
|
|
|
|
```javascript
|
|
layout: {
|
|
position: "left" | "center" | "right", // Where to position in bulk action bar
|
|
variant: "filled" | "outlined" | "text" // Visual style variant
|
|
}
|
|
```
|
|
|
|
#### Action Types
|
|
|
|
##### Global Actions (default behavior)
|
|
|
|
Global actions are displayed above the table and are always available:
|
|
|
|
```javascript
|
|
{
|
|
label: "Add New Item",
|
|
action: () => {
|
|
// Global action - no row data
|
|
console.log("Opening create modal");
|
|
},
|
|
icon: "pi pi-plus",
|
|
style: "primary"
|
|
// No requiresSelection, requiresMultipleSelection, or rowAction properties
|
|
}
|
|
```
|
|
|
|
##### Single Selection Actions (`requiresSelection: true`)
|
|
|
|
Single selection actions are displayed above the table but are only enabled when exactly one row is selected. They receive the selected row data as a parameter:
|
|
|
|
```javascript
|
|
{
|
|
label: "View Details",
|
|
action: (rowData) => {
|
|
// Single selection action - receives selected row data
|
|
console.log("Viewing:", rowData.name);
|
|
router.push(`/items/${rowData.id}`);
|
|
},
|
|
icon: "pi pi-eye",
|
|
style: "info",
|
|
requiresSelection: true
|
|
}
|
|
```
|
|
|
|
##### Row Actions (`rowAction: true`)
|
|
|
|
Row actions are displayed in an "Actions" column for each row and receive that row's data as a parameter:
|
|
|
|
```javascript
|
|
{
|
|
label: "Edit",
|
|
action: (rowData) => {
|
|
// Row action - receives individual row data
|
|
console.log("Editing:", rowData.name);
|
|
openEditModal(rowData);
|
|
},
|
|
icon: "pi pi-pencil",
|
|
style: "secondary",
|
|
rowAction: true
|
|
}
|
|
```
|
|
|
|
##### Bulk Actions (`requiresMultipleSelection: true`)
|
|
|
|
Bulk actions are displayed above the table when rows are selected and receive an array of selected row data:
|
|
|
|
```javascript
|
|
{
|
|
label: "Delete Selected",
|
|
action: (selectedRows) => {
|
|
// Bulk action - receives array of selected row data
|
|
console.log("Deleting:", selectedRows.length, "items");
|
|
selectedRows.forEach(row => deleteItem(row.id));
|
|
},
|
|
icon: "pi pi-trash",
|
|
style: "danger",
|
|
requiresMultipleSelection: true
|
|
}
|
|
```
|
|
|
|
### Example Table Actions Configuration
|
|
|
|
```javascript
|
|
const tableActions = [
|
|
// Global action - shows above table, always available
|
|
{
|
|
label: "Add Client",
|
|
action: () => modalStore.openModal("createClient"),
|
|
icon: "pi pi-plus",
|
|
style: "primary",
|
|
},
|
|
// Single selection action - shows above table, enabled when exactly one row selected
|
|
{
|
|
label: "View Details",
|
|
action: (rowData) => router.push(`/clients/${rowData.id}`),
|
|
icon: "pi pi-eye",
|
|
style: "info",
|
|
requiresSelection: true,
|
|
},
|
|
// Bulk action - shows when rows selected
|
|
{
|
|
label: "Delete Selected",
|
|
action: (selectedRows) => {
|
|
if (confirm(`Delete ${selectedRows.length} clients?`)) {
|
|
selectedRows.forEach((row) => deleteClient(row.id));
|
|
}
|
|
},
|
|
icon: "pi pi-trash",
|
|
style: "danger",
|
|
requiresMultipleSelection: true,
|
|
},
|
|
// Row actions - show in each row's actions column
|
|
{
|
|
label: "Edit",
|
|
action: (rowData) => editClient(rowData),
|
|
icon: "pi pi-pencil",
|
|
style: "secondary",
|
|
rowAction: true,
|
|
},
|
|
{
|
|
label: "Quick View",
|
|
action: (rowData) => showQuickPreview(rowData),
|
|
icon: "pi pi-search",
|
|
style: "info",
|
|
rowAction: true,
|
|
},
|
|
];
|
|
```
|
|
|
|
## 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**
|
|
|
|
### Table Actions
|
|
|
|
- **Global actions** displayed above the table for general operations
|
|
- **Row-specific actions** in dedicated actions column with row data access
|
|
- **Bulk actions** for selected rows with multi-selection support
|
|
- **Customizable button styles** with PrimeVue severity levels
|
|
- **Icon support** using PrimeVue icons
|
|
- **Automatic action handling** with error catching
|
|
- **Disabled state** during loading operations
|
|
- **Dynamic bulk action visibility** based on row selection
|
|
|
|
## 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>
|
|
```
|
|
|
|
### Interactive Table with Actions
|
|
|
|
```vue
|
|
<script setup>
|
|
import { useRouter } from "vue-router";
|
|
import { useModalStore } from "./stores/modal";
|
|
|
|
const router = useRouter();
|
|
const modalStore = useModalStore();
|
|
|
|
const columns = [
|
|
{ fieldName: "name", label: "Name", sortable: true, filterable: true },
|
|
{ fieldName: "status", label: "Status", type: "status", sortable: true },
|
|
{ fieldName: "email", label: "Email", filterable: true },
|
|
];
|
|
|
|
const data = [
|
|
{ id: 1, name: "John Doe", status: "completed", email: "john@example.com" },
|
|
{
|
|
id: 2,
|
|
name: "Jane Smith",
|
|
status: "in progress",
|
|
email: "jane@example.com",
|
|
},
|
|
];
|
|
|
|
const tableActions = [
|
|
// Global action
|
|
{
|
|
label: "Add User",
|
|
action: () => {
|
|
modalStore.openModal("createUser");
|
|
},
|
|
icon: "pi pi-plus",
|
|
style: "primary",
|
|
},
|
|
// Single selection action
|
|
{
|
|
label: "View Details",
|
|
action: (rowData) => {
|
|
router.push(`/users/${rowData.id}`);
|
|
},
|
|
icon: "pi pi-eye",
|
|
style: "info",
|
|
requiresSelection: true,
|
|
},
|
|
// Bulk actions
|
|
{
|
|
label: "Export Selected",
|
|
action: (selectedRows) => {
|
|
exportUsers(selectedRows);
|
|
},
|
|
icon: "pi pi-download",
|
|
style: "success",
|
|
requiresMultipleSelection: true,
|
|
},
|
|
{
|
|
label: "Delete Selected",
|
|
action: (selectedRows) => {
|
|
if (confirm(`Delete ${selectedRows.length} users?`)) {
|
|
bulkDeleteUsers(selectedRows.map((row) => row.id));
|
|
}
|
|
},
|
|
icon: "pi pi-trash",
|
|
style: "danger",
|
|
requiresMultipleSelection: true,
|
|
},
|
|
// Row actions
|
|
{
|
|
label: "Edit",
|
|
action: (rowData) => {
|
|
modalStore.openModal("editUser", rowData);
|
|
},
|
|
icon: "pi pi-pencil",
|
|
style: "secondary",
|
|
rowAction: true,
|
|
},
|
|
{
|
|
label: "Quick Actions",
|
|
action: (rowData) => {
|
|
showQuickActionsMenu(rowData);
|
|
},
|
|
icon: "pi pi-ellipsis-v",
|
|
style: "info",
|
|
rowAction: true,
|
|
},
|
|
];
|
|
|
|
const deleteUser = async (userId) => {
|
|
// API call to delete user
|
|
await Api.deleteUser(userId);
|
|
};
|
|
|
|
const bulkDeleteUsers = async (userIds) => {
|
|
// API call to delete multiple users
|
|
await Api.bulkDeleteUsers(userIds);
|
|
};
|
|
|
|
const exportUsers = (users) => {
|
|
// Export selected users to CSV/Excel
|
|
const csv = generateCSV(users);
|
|
downloadFile(csv, "users.csv");
|
|
};
|
|
|
|
const refreshData = () => {
|
|
// Refresh table data
|
|
location.reload();
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<DataTable
|
|
:columns="columns"
|
|
:data="data"
|
|
:tableActions="tableActions"
|
|
table-name="users-table"
|
|
/>
|
|
</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>
|
|
```
|
|
|
|
### Layout-Aware Actions Example
|
|
|
|
```vue
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useModalStore } from '@/stores/modal'
|
|
|
|
const router = useRouter()
|
|
const modalStore = useModalStore()
|
|
|
|
const columns = [
|
|
{ fieldName: 'name', label: 'Client Name', sortable: true, filterable: true },
|
|
{ fieldName: 'email', label: 'Email', filterable: true },
|
|
{ fieldName: 'status', label: 'Status', type: 'status', sortable: true }
|
|
]
|
|
|
|
const data = ref([
|
|
{ id: 1, name: 'Acme Corp', email: 'contact@acme.com', status: 'completed' },
|
|
{ id: 2, name: 'Tech Solutions', email: 'info@tech.com', status: 'in progress' }
|
|
])
|
|
|
|
const tableActions = [
|
|
// Left-positioned action with filled style
|
|
{
|
|
label: 'Quick Export',
|
|
icon: 'pi pi-download',
|
|
action: () => exportAllClients(),
|
|
severity: 'success',
|
|
layout: { position: 'left', variant: 'outlined' }
|
|
},
|
|
// Center-positioned bulk action
|
|
{
|
|
label: 'Archive Selected',
|
|
icon: 'pi pi-archive',
|
|
action: (selectedRows) => archiveClients(selectedRows),
|
|
severity: 'warning',
|
|
requiresMultipleSelection: true,
|
|
layout: { position: 'center', variant: 'outlined' }
|
|
},
|
|
// Right-positioned main action
|
|
{
|
|
label: 'Create Client',
|
|
icon: 'pi pi-plus',
|
|
action: () => modalStore.openModal('createClient'),
|
|
severity: 'info',
|
|
layout: { position: 'right', variant: 'filled' }
|
|
},
|
|
// Single selection action
|
|
{
|
|
label: 'Edit Details',
|
|
icon: 'pi pi-pencil',
|
|
action: (rowData) => router.push(`/clients/${rowData.id}/edit`),
|
|
severity: 'secondary',
|
|
requiresSelection: true,
|
|
layout: { position: 'left', variant: 'text' }
|
|
},
|
|
// Primary row action - most important
|
|
{
|
|
label: 'View',
|
|
icon: 'pi pi-eye',
|
|
action: (rowData) => router.push(`/clients/${rowData.id}`),
|
|
severity: 'info',
|
|
rowAction: true,
|
|
layout: { priority: 'primary', variant: 'outlined' }
|
|
},
|
|
// Secondary row action - less important
|
|
{
|
|
label: 'Contact',
|
|
icon: 'pi pi-phone',
|
|
action: (rowData) => initiateContact(rowData),
|
|
severity: 'success',
|
|
rowAction: true,
|
|
layout: { priority: 'secondary', variant: 'text' }
|
|
},
|
|
// Dropdown row action - additional options
|
|
{
|
|
label: 'More',
|
|
icon: 'pi pi-ellipsis-v',
|
|
action: (rowData) => showMoreOptions(rowData),
|
|
rowAction: true,
|
|
layout: { priority: 'dropdown', variant: 'icon-only' }
|
|
}
|
|
]
|
|
|
|
const exportAllClients = () => {
|
|
// Export logic
|
|
}
|
|
|
|
const archiveClients = (clients) => {
|
|
// Archive logic
|
|
}
|
|
|
|
const initiateContact = (client) => {
|
|
// Contact logic
|
|
}
|
|
|
|
const showMoreOptions = (client) => {
|
|
// More options logic
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<DataTable
|
|
:columns="columns"
|
|
:data="data"
|
|
:tableActions="tableActions"
|
|
table-name="clients-with-layout"
|
|
/>
|
|
</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`)
|