update filter functionality
This commit is contained in:
parent
464c62d1e5
commit
9431a0502a
10 changed files with 2362 additions and 220 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Overview
|
||||
|
||||
A feature-rich data table component built with PrimeVue's DataTable. This component provides advanced functionality including pagination, sorting, filtering, row selection, and customizable column types with persistent filter state management.
|
||||
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
|
||||
|
||||
|
|
@ -17,75 +17,119 @@ A feature-rich data table component built with PrimeVue's DataTable. This compon
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import DataTable from './components/common/DataTable.vue'
|
||||
import { ref } from "vue";
|
||||
import DataTable from "./components/common/DataTable.vue";
|
||||
|
||||
const tableColumns = ref([
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: 'Name',
|
||||
fieldName: "name",
|
||||
label: "Name",
|
||||
sortable: true,
|
||||
filterable: true
|
||||
filterable: true,
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: 'Status',
|
||||
type: 'status',
|
||||
fieldName: "status",
|
||||
label: "Status",
|
||||
type: "status",
|
||||
sortable: true,
|
||||
filterable: true
|
||||
}
|
||||
])
|
||||
filterable: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const tableData = ref([
|
||||
{ id: 1, name: 'John Doe', status: 'completed' },
|
||||
{ id: 2, name: 'Jane Smith', status: 'in progress' }
|
||||
])
|
||||
{ id: 1, name: "John Doe", status: "completed" },
|
||||
{ id: 2, name: "Jane Smith", status: "in progress" },
|
||||
]);
|
||||
|
||||
const handleRowClick = (event) => {
|
||||
console.log('Row clicked:', event.data)
|
||||
}
|
||||
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
|
||||
|
||||
- **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',
|
||||
|
|
@ -97,13 +141,16 @@ Renders values as colored tags/badges:
|
|||
```
|
||||
|
||||
**Status Colors:**
|
||||
|
||||
- `'completed'` → Success (green)
|
||||
- `'in progress'` → Warning (yellow/orange)
|
||||
- `'in progress'` → Warning (yellow/orange)
|
||||
- `'not started'` → Danger (red)
|
||||
- Other values → Info (blue)
|
||||
|
||||
##### `'button'` Type
|
||||
|
||||
Renders values as clickable buttons:
|
||||
|
||||
```javascript
|
||||
{
|
||||
fieldName: 'action',
|
||||
|
|
@ -115,120 +162,237 @@ Renders values as clickable buttons:
|
|||
## 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)
|
||||
}
|
||||
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
|
||||
- **Row-level filtering** for filterable columns
|
||||
- **Text-based search** with real-time filtering
|
||||
- **Persistent filter state** across component re-renders
|
||||
- **Global search capability**
|
||||
|
||||
- **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
|
||||
|
||||
### Basic Table
|
||||
### Server-Side Paginated Table (Recommended for Large Datasets)
|
||||
|
||||
```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 }
|
||||
]
|
||||
import { ref } from "vue";
|
||||
import DataTable from "./components/common/DataTable.vue";
|
||||
import Api from "./api.js";
|
||||
|
||||
const data = [
|
||||
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
||||
]
|
||||
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"
|
||||
:data="data"
|
||||
table-name="users-table"
|
||||
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 }
|
||||
]
|
||||
{ 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' }
|
||||
]
|
||||
{ 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"
|
||||
/>
|
||||
<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' }
|
||||
]
|
||||
{ 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' }
|
||||
]
|
||||
{ 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}`)
|
||||
const { data, index } = slotProps;
|
||||
console.log(`Action clicked for ${data.name} at row ${index}`);
|
||||
// Handle the action (navigate, open modal, etc.)
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -242,14 +406,15 @@ const handleRowClick = (slotProps) => {
|
|||
```
|
||||
|
||||
### Custom Filters
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { FilterMatchMode } from '@primevue/core'
|
||||
import { FilterMatchMode } from "@primevue/core";
|
||||
|
||||
const customFilters = {
|
||||
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
|
||||
name: { value: 'John', matchMode: FilterMatchMode.STARTS_WITH }
|
||||
}
|
||||
name: { value: "John", matchMode: FilterMatchMode.STARTS_WITH },
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -267,11 +432,13 @@ const customFilters = {
|
|||
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
|
||||
- `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`
|
||||
|
|
@ -279,6 +446,7 @@ The component integrates with a Pinia store (`useFiltersStore`) for persistent f
|
|||
## 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
|
||||
|
|
@ -287,11 +455,13 @@ The component uses PrimeVue's default DataTable styling with:
|
|||
## 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
|
||||
|
|
@ -310,6 +480,7 @@ The component uses PrimeVue's default DataTable styling with:
|
|||
## Accessibility
|
||||
|
||||
The component includes:
|
||||
|
||||
- **Keyboard navigation** support via PrimeVue
|
||||
- **Screen reader compatibility** with proper ARIA labels
|
||||
- **High contrast** status badges for visibility
|
||||
|
|
@ -319,6 +490,7 @@ The component includes:
|
|||
## Browser Support
|
||||
|
||||
Compatible with all modern browsers that support:
|
||||
|
||||
- Vue 3 Composition API
|
||||
- ES6+ features
|
||||
- CSS Grid and Flexbox
|
||||
|
|
@ -329,4 +501,4 @@ Compatible with all modern browsers that support:
|
|||
- **Vue 3** with Composition API
|
||||
- **PrimeVue** DataTable, Column, Tag, Button, InputText components
|
||||
- **@primevue/core** for FilterMatchMode
|
||||
- **Pinia** store for state management (`useFiltersStore`)
|
||||
- **Pinia** store for state management (`useFiltersStore`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue