ngx-column-filter-popup 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCUMENTATION.md +690 -0
- package/LICENSE +21 -0
- package/README.md +451 -0
- package/USAGE_EXAMPLES.md +251 -0
- package/package.json +86 -0
- package/src/app/components/column-filter/column-filter.component.html +131 -0
- package/src/app/components/column-filter/column-filter.component.scss +426 -0
- package/src/app/components/column-filter/column-filter.component.ts +445 -0
- package/src/app/components/column-filter/column-filter.module.ts +31 -0
- package/src/app/lib/models/filter.models.ts +51 -0
- package/src/app/lib/public-api.ts +21 -0
- package/src/app/lib/services/column-filter.service.ts +35 -0
- package/src/app/lib/utils/column-filter.utils.ts +236 -0
package/DOCUMENTATION.md
ADDED
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
# Column Filter Library - Complete Documentation
|
|
2
|
+
|
|
3
|
+
## What is This Component?
|
|
4
|
+
|
|
5
|
+
This is a **reusable Angular column filter component** that provides advanced filtering capabilities for tables, lists, or any data grid.
|
|
6
|
+
|
|
7
|
+
### Key Features:
|
|
8
|
+
|
|
9
|
+
1. **Multiple Filter Rules**: Add multiple filter conditions per column
|
|
10
|
+
2. **Multiple Field Types**: Specialized filters for different data types:
|
|
11
|
+
- **Text** - For text fields (default)
|
|
12
|
+
- **Currency** - For currency values (with currency symbol)
|
|
13
|
+
- **Age/Number** - For numeric values (simple number input)
|
|
14
|
+
- **Date** - For date values (date picker)
|
|
15
|
+
- **Status** - For predefined status options (dropdown)
|
|
16
|
+
|
|
17
|
+
3. **Various Match Types**: Different matching options available based on field type:
|
|
18
|
+
- **Text Fields**: Match All, Match Any, Starts with, Ends with, Contains, Equals
|
|
19
|
+
- **Currency/Age Fields**: Equals, Greater than, Less than, Greater or equal, Less or equal
|
|
20
|
+
- **Date Fields**: Date is, Date is not, Date is before, Date is after, Date is on
|
|
21
|
+
- **Status Fields**: Is, Is not, Equals
|
|
22
|
+
|
|
23
|
+
4. **Global Match Mode**:
|
|
24
|
+
- **Match All Rules** (AND Logic): When multiple rules exist, **all rules** must match
|
|
25
|
+
- **Match Any Rule** (OR Logic): When multiple rules exist, **any one rule** can match (Default)
|
|
26
|
+
|
|
27
|
+
5. **Visual Feedback**: Filter icon changes when active
|
|
28
|
+
6. **Fully Customizable**: Easily configure according to your data
|
|
29
|
+
7. **Single Filter Open**: Only one filter dropdown can be open at a time
|
|
30
|
+
8. **ESC Key Support**: Press ESC to close the open filter
|
|
31
|
+
9. **Programmatic Control**: Clear filters programmatically using `clearFilter()` method
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## How to Use?
|
|
36
|
+
|
|
37
|
+
### Basic Usage:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { Component } from '@angular/core';
|
|
41
|
+
import { ColumnFilterComponent } from 'ngx-column-filter-popup';
|
|
42
|
+
import { FilterConfig, applyColumnFilter } from 'ngx-column-filter-popup';
|
|
43
|
+
|
|
44
|
+
interface User {
|
|
45
|
+
id: number;
|
|
46
|
+
firstName: string;
|
|
47
|
+
lastName: string;
|
|
48
|
+
email: string;
|
|
49
|
+
role: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Component({
|
|
53
|
+
selector: 'app-user-list',
|
|
54
|
+
imports: [ColumnFilterComponent],
|
|
55
|
+
template: `
|
|
56
|
+
<table>
|
|
57
|
+
<thead>
|
|
58
|
+
<tr>
|
|
59
|
+
<th>
|
|
60
|
+
First Name
|
|
61
|
+
<lib-column-filter
|
|
62
|
+
columnName="first name"
|
|
63
|
+
columnKey="firstName"
|
|
64
|
+
(filterApplied)="onFirstNameFilter($event)"
|
|
65
|
+
(filterCleared)="onFirstNameClear()">
|
|
66
|
+
</lib-column-filter>
|
|
67
|
+
</th>
|
|
68
|
+
<th>
|
|
69
|
+
Last Name
|
|
70
|
+
<lib-column-filter
|
|
71
|
+
columnName="last name"
|
|
72
|
+
columnKey="lastName"
|
|
73
|
+
(filterApplied)="onLastNameFilter($event)"
|
|
74
|
+
(filterCleared)="onLastNameClear()">
|
|
75
|
+
</lib-column-filter>
|
|
76
|
+
</th>
|
|
77
|
+
</tr>
|
|
78
|
+
</thead>
|
|
79
|
+
<tbody>
|
|
80
|
+
<tr *ngFor="let user of filteredUsers">
|
|
81
|
+
<td>{{ user.firstName }}</td>
|
|
82
|
+
<td>{{ user.lastName }}</td>
|
|
83
|
+
</tr>
|
|
84
|
+
</tbody>
|
|
85
|
+
</table>
|
|
86
|
+
`
|
|
87
|
+
})
|
|
88
|
+
export class UserListComponent {
|
|
89
|
+
users: User[] = [
|
|
90
|
+
{ id: 1, firstName: 'John', lastName: 'Doe', email: 'john@example.com', role: 'Developer' },
|
|
91
|
+
{ id: 2, firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com', role: 'Designer' }
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
filteredUsers: User[] = [...this.users];
|
|
95
|
+
firstNameFilter: FilterConfig | null = null;
|
|
96
|
+
lastNameFilter: FilterConfig | null = null;
|
|
97
|
+
|
|
98
|
+
onFirstNameFilter(filterConfig: FilterConfig) {
|
|
99
|
+
this.firstNameFilter = filterConfig;
|
|
100
|
+
this.applyFilters();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onFirstNameClear() {
|
|
104
|
+
this.firstNameFilter = null;
|
|
105
|
+
this.applyFilters();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
onLastNameFilter(filterConfig: FilterConfig) {
|
|
109
|
+
this.lastNameFilter = filterConfig;
|
|
110
|
+
this.applyFilters();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onLastNameClear() {
|
|
114
|
+
this.lastNameFilter = null;
|
|
115
|
+
this.applyFilters();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private applyFilters() {
|
|
119
|
+
let result = [...this.users];
|
|
120
|
+
|
|
121
|
+
// Apply firstName filter
|
|
122
|
+
if (this.firstNameFilter) {
|
|
123
|
+
result = applyColumnFilter(result, 'firstName', this.firstNameFilter);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Apply lastName filter
|
|
127
|
+
if (this.lastNameFilter) {
|
|
128
|
+
result = applyColumnFilter(result, 'lastName', this.lastNameFilter);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.filteredUsers = result;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Data Adaptation
|
|
139
|
+
|
|
140
|
+
When you change your **original data**, the component will automatically adapt. You only need to update **two things in HTML**:
|
|
141
|
+
|
|
142
|
+
1. **`columnKey`**: The property name to filter on
|
|
143
|
+
- Example: If your data has `name` field, use `columnKey="name"`
|
|
144
|
+
|
|
145
|
+
2. **`columnName`**: The display name shown to users (used in placeholder)
|
|
146
|
+
- Example: `columnName="employee name"` or `columnName="customer name"`
|
|
147
|
+
|
|
148
|
+
### Example - Adapting to Data Changes:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Your previous data:
|
|
152
|
+
interface OldData {
|
|
153
|
+
firstName: string;
|
|
154
|
+
lastName: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Your new data:
|
|
158
|
+
interface NewData {
|
|
159
|
+
employeeName: string;
|
|
160
|
+
department: string;
|
|
161
|
+
salary: number;
|
|
162
|
+
location: string;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Update HTML:**
|
|
167
|
+
```html
|
|
168
|
+
<!-- Old -->
|
|
169
|
+
<lib-column-filter
|
|
170
|
+
columnName="first name"
|
|
171
|
+
columnKey="firstName"
|
|
172
|
+
(filterApplied)="onFirstNameFilter($event)">
|
|
173
|
+
</lib-column-filter>
|
|
174
|
+
|
|
175
|
+
<!-- New -->
|
|
176
|
+
<lib-column-filter
|
|
177
|
+
columnName="employee name"
|
|
178
|
+
columnKey="employeeName"
|
|
179
|
+
(filterApplied)="onEmployeeNameFilter($event)">
|
|
180
|
+
</lib-column-filter>
|
|
181
|
+
|
|
182
|
+
<!-- New Department filter -->
|
|
183
|
+
<lib-column-filter
|
|
184
|
+
columnName="department"
|
|
185
|
+
columnKey="department"
|
|
186
|
+
(filterApplied)="onDepartmentFilter($event)">
|
|
187
|
+
</lib-column-filter>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Update TypeScript:**
|
|
191
|
+
```typescript
|
|
192
|
+
// Use the same columnKey in utility function that matches HTML
|
|
193
|
+
if (this.employeeNameFilter) {
|
|
194
|
+
result = applyColumnFilter(result, 'employeeName', this.employeeNameFilter);
|
|
195
|
+
// ↑ This columnKey should match the one in HTML
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Field Types
|
|
202
|
+
|
|
203
|
+
This library supports **5 different field types**:
|
|
204
|
+
|
|
205
|
+
### 1. Text Field (Default)
|
|
206
|
+
|
|
207
|
+
**Use Case**: Text content like names, emails, descriptions, etc.
|
|
208
|
+
|
|
209
|
+
**Match Types Available**:
|
|
210
|
+
- Match All (all words must be present)
|
|
211
|
+
- Match Any (at least one word must be present)
|
|
212
|
+
- Starts with
|
|
213
|
+
- Ends with
|
|
214
|
+
- Contains
|
|
215
|
+
- Equals
|
|
216
|
+
|
|
217
|
+
**Example**:
|
|
218
|
+
```html
|
|
219
|
+
<lib-column-filter
|
|
220
|
+
columnName="first name"
|
|
221
|
+
columnKey="firstName"
|
|
222
|
+
fieldType="text"
|
|
223
|
+
(filterApplied)="onFilterApplied($event)">
|
|
224
|
+
</lib-column-filter>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### 2. Currency Field
|
|
230
|
+
|
|
231
|
+
**Use Case**: Currency values like price, balance, amount, etc.
|
|
232
|
+
|
|
233
|
+
**Match Types Available**:
|
|
234
|
+
- Equals
|
|
235
|
+
- Greater than
|
|
236
|
+
- Less than
|
|
237
|
+
- Greater or equal
|
|
238
|
+
- Less or equal
|
|
239
|
+
|
|
240
|
+
**Features**:
|
|
241
|
+
- Currency symbol display ($, ₹, €, etc.)
|
|
242
|
+
- Automatic number formatting with commas
|
|
243
|
+
- Decimal support
|
|
244
|
+
|
|
245
|
+
**Example**:
|
|
246
|
+
```html
|
|
247
|
+
<lib-column-filter
|
|
248
|
+
columnName="balance"
|
|
249
|
+
columnKey="balance"
|
|
250
|
+
fieldType="currency"
|
|
251
|
+
currencySymbol="$"
|
|
252
|
+
(filterApplied)="onBalanceFilterApplied($event)">
|
|
253
|
+
</lib-column-filter>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### 3. Age/Number Field
|
|
259
|
+
|
|
260
|
+
**Use Case**: Simple numeric values like age, quantity, count, ratings, etc.
|
|
261
|
+
|
|
262
|
+
**Match Types Available**:
|
|
263
|
+
- Equals
|
|
264
|
+
- Greater than
|
|
265
|
+
- Less than
|
|
266
|
+
- Greater or equal
|
|
267
|
+
- Less or equal
|
|
268
|
+
|
|
269
|
+
**Features**:
|
|
270
|
+
- Simple number input (no currency symbol)
|
|
271
|
+
- Integer support
|
|
272
|
+
- Easy to use for age, quantity, etc.
|
|
273
|
+
|
|
274
|
+
**Example**:
|
|
275
|
+
```html
|
|
276
|
+
<lib-column-filter
|
|
277
|
+
columnName="age"
|
|
278
|
+
columnKey="age"
|
|
279
|
+
fieldType="age"
|
|
280
|
+
(filterApplied)="onAgeFilterApplied($event)">
|
|
281
|
+
</lib-column-filter>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
### 4. Date Field
|
|
287
|
+
|
|
288
|
+
**Use Case**: Date values like created date, due date, birth date, etc.
|
|
289
|
+
|
|
290
|
+
**Match Types Available**:
|
|
291
|
+
- Date is
|
|
292
|
+
- Date is not
|
|
293
|
+
- Date is before
|
|
294
|
+
- Date is after
|
|
295
|
+
- Date is on
|
|
296
|
+
|
|
297
|
+
**Features**:
|
|
298
|
+
- Native date picker
|
|
299
|
+
- Proper date comparison
|
|
300
|
+
- Date normalization for accurate filtering
|
|
301
|
+
|
|
302
|
+
**Example**:
|
|
303
|
+
```html
|
|
304
|
+
<lib-column-filter
|
|
305
|
+
columnName="date"
|
|
306
|
+
columnKey="date"
|
|
307
|
+
fieldType="date"
|
|
308
|
+
(filterApplied)="onDateFilterApplied($event)">
|
|
309
|
+
</lib-column-filter>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### 5. Status Field
|
|
315
|
+
|
|
316
|
+
**Use Case**: Predefined status options like order status, user status, task status, etc.
|
|
317
|
+
|
|
318
|
+
**Match Types Available**:
|
|
319
|
+
- Is
|
|
320
|
+
- Is not
|
|
321
|
+
- Equals
|
|
322
|
+
|
|
323
|
+
**Features**:
|
|
324
|
+
- Dropdown with predefined options
|
|
325
|
+
- Clean UI with select dropdown
|
|
326
|
+
- Easy status selection
|
|
327
|
+
|
|
328
|
+
**Example**:
|
|
329
|
+
```html
|
|
330
|
+
<lib-column-filter
|
|
331
|
+
columnName="status"
|
|
332
|
+
columnKey="status"
|
|
333
|
+
fieldType="status"
|
|
334
|
+
[statusOptions]="['qualified', 'unqualified', 'negotiation', 'new']"
|
|
335
|
+
(filterApplied)="onStatusFilterApplied($event)">
|
|
336
|
+
</lib-column-filter>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
### Complete Example with All Field Types
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { Component } from '@angular/core';
|
|
345
|
+
import { ColumnFilterComponent } from 'ngx-column-filter-popup';
|
|
346
|
+
import { FilterConfig, applyColumnFilter } from 'ngx-column-filter-popup';
|
|
347
|
+
|
|
348
|
+
interface Employee {
|
|
349
|
+
id: number;
|
|
350
|
+
name: string;
|
|
351
|
+
age: number;
|
|
352
|
+
salary: number;
|
|
353
|
+
joinDate: string;
|
|
354
|
+
status: string;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@Component({
|
|
358
|
+
selector: 'app-employee-list',
|
|
359
|
+
imports: [ColumnFilterComponent],
|
|
360
|
+
template: `
|
|
361
|
+
<table>
|
|
362
|
+
<thead>
|
|
363
|
+
<tr>
|
|
364
|
+
<th>
|
|
365
|
+
Name
|
|
366
|
+
<lib-column-filter
|
|
367
|
+
columnName="name"
|
|
368
|
+
columnKey="name"
|
|
369
|
+
fieldType="text"
|
|
370
|
+
(filterApplied)="onNameFilter($event)">
|
|
371
|
+
</lib-column-filter>
|
|
372
|
+
</th>
|
|
373
|
+
<th>
|
|
374
|
+
Age
|
|
375
|
+
<lib-column-filter
|
|
376
|
+
columnName="age"
|
|
377
|
+
columnKey="age"
|
|
378
|
+
fieldType="age"
|
|
379
|
+
(filterApplied)="onAgeFilter($event)">
|
|
380
|
+
</lib-column-filter>
|
|
381
|
+
</th>
|
|
382
|
+
<th>
|
|
383
|
+
Salary
|
|
384
|
+
<lib-column-filter
|
|
385
|
+
columnName="salary"
|
|
386
|
+
columnKey="salary"
|
|
387
|
+
fieldType="currency"
|
|
388
|
+
currencySymbol="$"
|
|
389
|
+
(filterApplied)="onSalaryFilter($event)">
|
|
390
|
+
</lib-column-filter>
|
|
391
|
+
</th>
|
|
392
|
+
<th>
|
|
393
|
+
Join Date
|
|
394
|
+
<lib-column-filter
|
|
395
|
+
columnName="join date"
|
|
396
|
+
columnKey="joinDate"
|
|
397
|
+
fieldType="date"
|
|
398
|
+
(filterApplied)="onDateFilter($event)">
|
|
399
|
+
</lib-column-filter>
|
|
400
|
+
</th>
|
|
401
|
+
<th>
|
|
402
|
+
Status
|
|
403
|
+
<lib-column-filter
|
|
404
|
+
columnName="status"
|
|
405
|
+
columnKey="status"
|
|
406
|
+
fieldType="status"
|
|
407
|
+
[statusOptions]="statusOptions"
|
|
408
|
+
(filterApplied)="onStatusFilter($event)">
|
|
409
|
+
</lib-column-filter>
|
|
410
|
+
</th>
|
|
411
|
+
</tr>
|
|
412
|
+
</thead>
|
|
413
|
+
<tbody>
|
|
414
|
+
<tr *ngFor="let emp of filteredEmployees">
|
|
415
|
+
<td>{{ emp.name }}</td>
|
|
416
|
+
<td>{{ emp.age }}</td>
|
|
417
|
+
<td>${{ emp.salary | number }}</td>
|
|
418
|
+
<td>{{ emp.joinDate }}</td>
|
|
419
|
+
<td>{{ emp.status }}</td>
|
|
420
|
+
</tr>
|
|
421
|
+
</tbody>
|
|
422
|
+
</table>
|
|
423
|
+
`
|
|
424
|
+
})
|
|
425
|
+
export class EmployeeListComponent {
|
|
426
|
+
employees: Employee[] = [
|
|
427
|
+
{ id: 1, name: 'John Doe', age: 30, salary: 50000, joinDate: '2020-01-15', status: 'active' },
|
|
428
|
+
{ id: 2, name: 'Jane Smith', age: 25, salary: 60000, joinDate: '2019-05-20', status: 'active' }
|
|
429
|
+
];
|
|
430
|
+
|
|
431
|
+
filteredEmployees: Employee[] = [...this.employees];
|
|
432
|
+
statusOptions = ['active', 'inactive', 'on-leave', 'terminated'];
|
|
433
|
+
|
|
434
|
+
nameFilter: FilterConfig | null = null;
|
|
435
|
+
ageFilter: FilterConfig | null = null;
|
|
436
|
+
salaryFilter: FilterConfig | null = null;
|
|
437
|
+
dateFilter: FilterConfig | null = null;
|
|
438
|
+
statusFilter: FilterConfig | null = null;
|
|
439
|
+
|
|
440
|
+
onNameFilter(filterConfig: FilterConfig) {
|
|
441
|
+
this.nameFilter = filterConfig;
|
|
442
|
+
this.applyAllFilters();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
onAgeFilter(filterConfig: FilterConfig) {
|
|
446
|
+
this.ageFilter = filterConfig;
|
|
447
|
+
this.applyAllFilters();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
onSalaryFilter(filterConfig: FilterConfig) {
|
|
451
|
+
this.salaryFilter = filterConfig;
|
|
452
|
+
this.applyAllFilters();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
onDateFilter(filterConfig: FilterConfig) {
|
|
456
|
+
this.dateFilter = filterConfig;
|
|
457
|
+
this.applyAllFilters();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
onStatusFilter(filterConfig: FilterConfig) {
|
|
461
|
+
this.statusFilter = filterConfig;
|
|
462
|
+
this.applyAllFilters();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private applyAllFilters() {
|
|
466
|
+
let result = [...this.employees];
|
|
467
|
+
|
|
468
|
+
if (this.nameFilter) {
|
|
469
|
+
result = applyColumnFilter(result, 'name', this.nameFilter);
|
|
470
|
+
}
|
|
471
|
+
if (this.ageFilter) {
|
|
472
|
+
result = applyColumnFilter(result, 'age', this.ageFilter);
|
|
473
|
+
}
|
|
474
|
+
if (this.salaryFilter) {
|
|
475
|
+
result = applyColumnFilter(result, 'salary', this.salaryFilter);
|
|
476
|
+
}
|
|
477
|
+
if (this.dateFilter) {
|
|
478
|
+
result = applyColumnFilter(result, 'joinDate', this.dateFilter);
|
|
479
|
+
}
|
|
480
|
+
if (this.statusFilter) {
|
|
481
|
+
result = applyColumnFilter(result, 'status', this.statusFilter);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
this.filteredEmployees = result;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Match All Rules Feature
|
|
492
|
+
|
|
493
|
+
### What is it?
|
|
494
|
+
|
|
495
|
+
When you add **multiple filter rules**, you can choose how to combine them:
|
|
496
|
+
- **Match Any Rule** (default): If any rule matches, the item is included (OR logic)
|
|
497
|
+
- **Match All Rules**: All rules must match for the item to be included (AND logic)
|
|
498
|
+
|
|
499
|
+
### Example:
|
|
500
|
+
|
|
501
|
+
Let's say you have 2 rules:
|
|
502
|
+
- Rule 1: Contains "John"
|
|
503
|
+
- Rule 2: Contains "Doe"
|
|
504
|
+
|
|
505
|
+
#### Match Any Rule (OR Logic):
|
|
506
|
+
- ✅ "John Smith" will match (Rule 1 matches)
|
|
507
|
+
- ✅ "Jane Doe" will match (Rule 2 matches)
|
|
508
|
+
- ✅ "John Doe" will match (both rules match)
|
|
509
|
+
|
|
510
|
+
#### Match All Rules (AND Logic):
|
|
511
|
+
- ❌ "John Smith" won't match (Rule 2 fails)
|
|
512
|
+
- ❌ "Jane Doe" won't match (Rule 1 fails)
|
|
513
|
+
- ✅ "John Doe" will match (both rules match)
|
|
514
|
+
|
|
515
|
+
### UI Appearance:
|
|
516
|
+
|
|
517
|
+
When you add **2 or more rules**, a **"Match All Rules" / "Match Any Rule"** toggle will appear at the top of the dropdown:
|
|
518
|
+
|
|
519
|
+
- **Green border and light green background**: When "Match All Rules" is selected
|
|
520
|
+
- **Normal border**: When "Match Any Rule" is selected (default)
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Component API Reference
|
|
525
|
+
|
|
526
|
+
### Inputs / Input Properties:
|
|
527
|
+
|
|
528
|
+
| Input | Type | Default | Description |
|
|
529
|
+
|-------|------|---------|-------------|
|
|
530
|
+
| `columnName` | `string` | `''` | Display name of the column (used in placeholder) |
|
|
531
|
+
| `columnKey` | `string` | `''` | Property name to filter on |
|
|
532
|
+
| `fieldType` | `FieldType` | `'text'` | Field type: 'text', 'currency', 'age', 'date', or 'status' |
|
|
533
|
+
| `currencySymbol` | `string` | `'$'` | Currency symbol for currency field type (optional) |
|
|
534
|
+
| `statusOptions` | `string[]` | `[]` | Array of status options for status field type (required for status) |
|
|
535
|
+
| `initialFilter` | `FilterConfig?` | `undefined` | Initial filter configuration (optional) |
|
|
536
|
+
| `placeholder` | `string?` | `undefined` | Custom placeholder text. Default: "Search by {columnName}" |
|
|
537
|
+
| `availableMatchTypes` | `MatchType[]?` | `undefined` | Customize available match types (optional) |
|
|
538
|
+
|
|
539
|
+
### Outputs / Output Events:
|
|
540
|
+
|
|
541
|
+
| Output | Type | Description |
|
|
542
|
+
|--------|------|-------------|
|
|
543
|
+
| `filterApplied` | `EventEmitter<FilterConfig>` | Emitted when filter is applied |
|
|
544
|
+
| `filterCleared` | `EventEmitter<void>` | Emitted when filter is cleared |
|
|
545
|
+
|
|
546
|
+
### Public Methods:
|
|
547
|
+
|
|
548
|
+
| Method | Description |
|
|
549
|
+
|--------|-------------|
|
|
550
|
+
| `clearFilter()` | Programmatically clear all filter rules and reset the filter |
|
|
551
|
+
|
|
552
|
+
### FilterConfig Interface:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
type FieldType = 'text' | 'currency' | 'age' | 'date' | 'status';
|
|
556
|
+
|
|
557
|
+
interface FilterConfig {
|
|
558
|
+
rules: FilterRule[];
|
|
559
|
+
globalMatchMode?: GlobalMatchMode; // 'match-all-rules' | 'match-any-rule'
|
|
560
|
+
fieldType?: FieldType; // Field type for this filter
|
|
561
|
+
statusOptions?: string[]; // Status options (for status field type)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
interface FilterRule {
|
|
565
|
+
id: string;
|
|
566
|
+
matchType: MatchType;
|
|
567
|
+
value: string;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Match types vary based on field type:
|
|
571
|
+
// Text: 'match-all' | 'match-any' | 'starts-with' | 'ends-with' | 'contains' | 'equals'
|
|
572
|
+
// Currency/Age: 'equals' | 'greater-than' | 'less-than' | 'greater-equal' | 'less-equal'
|
|
573
|
+
// Date: 'is' | 'is-not' | 'is-before' | 'is-after' | 'is-on'
|
|
574
|
+
// Status: 'is' | 'is-not' | 'equals'
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Utility Functions
|
|
580
|
+
|
|
581
|
+
### applyColumnFilter
|
|
582
|
+
|
|
583
|
+
Apply filter rules to a dataset:
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
import { applyColumnFilter } from 'ngx-column-filter-popup';
|
|
587
|
+
|
|
588
|
+
const filteredData = applyColumnFilter(
|
|
589
|
+
data, // Your data array
|
|
590
|
+
'columnKey', // Property name to filter on
|
|
591
|
+
filterConfig // FilterConfig from component
|
|
592
|
+
);
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### itemMatchesFilter
|
|
596
|
+
|
|
597
|
+
Check if a single item matches filter rules:
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
import { itemMatchesFilter } from 'ngx-column-filter-popup';
|
|
601
|
+
|
|
602
|
+
const matches = itemMatchesFilter(
|
|
603
|
+
item, // Single item to check
|
|
604
|
+
'columnKey', // Property name to filter on
|
|
605
|
+
filterConfig // FilterConfig from component
|
|
606
|
+
);
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Programmatic Control
|
|
612
|
+
|
|
613
|
+
### Clearing Filters Programmatically
|
|
614
|
+
|
|
615
|
+
You can clear filters programmatically using `ViewChild`:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
import { Component, ViewChild } from '@angular/core';
|
|
619
|
+
import { ColumnFilterComponent } from 'ngx-column-filter-popup';
|
|
620
|
+
|
|
621
|
+
@Component({
|
|
622
|
+
template: `
|
|
623
|
+
<lib-column-filter
|
|
624
|
+
#nameFilter
|
|
625
|
+
columnName="name"
|
|
626
|
+
columnKey="name">
|
|
627
|
+
</lib-column-filter>
|
|
628
|
+
<button (click)="clearFilter()">Clear Filter</button>
|
|
629
|
+
`
|
|
630
|
+
})
|
|
631
|
+
export class ExampleComponent {
|
|
632
|
+
@ViewChild('nameFilter') filter!: ColumnFilterComponent;
|
|
633
|
+
|
|
634
|
+
clearFilter() {
|
|
635
|
+
this.filter.clearFilter(); // Programmatically clear the filter
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## Important Notes:
|
|
643
|
+
|
|
644
|
+
1. **Data Type Independence**: Component works with any data type, just ensure `columnKey` is correct.
|
|
645
|
+
|
|
646
|
+
2. **Case Insensitive**: All filters are case-insensitive (uppercase/lowercase doesn't matter)
|
|
647
|
+
|
|
648
|
+
3. **Empty Rules**: Empty rules are automatically ignored
|
|
649
|
+
|
|
650
|
+
4. **Multiple Columns**: You can apply filters on as many columns as you want
|
|
651
|
+
|
|
652
|
+
5. **Filter Combination**: Filters from multiple columns are combined with **AND logic** (all column filters must match)
|
|
653
|
+
|
|
654
|
+
6. **Global Match Mode**: This only applies to **multiple rules within one column**, not multiple columns
|
|
655
|
+
|
|
656
|
+
7. **Single Filter Open**: Only one filter dropdown can be open at a time - opening a new filter closes the previous one automatically
|
|
657
|
+
|
|
658
|
+
8. **ESC Key**: Press ESC key to close the currently open filter dropdown
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Troubleshooting
|
|
663
|
+
|
|
664
|
+
### Problem: Filter not working
|
|
665
|
+
- Check that `columnKey` matches your data object's property name
|
|
666
|
+
- Check that `applyColumnFilter` function uses the same `columnKey`
|
|
667
|
+
|
|
668
|
+
### Problem: "Match All Rules" toggle not showing
|
|
669
|
+
- This only appears when you have **2 or more rules**
|
|
670
|
+
- Use the "Add Rule" button to add more rules
|
|
671
|
+
|
|
672
|
+
### Problem: Data not updating after data change
|
|
673
|
+
- Remember to update `columnKey` and `columnName` in HTML
|
|
674
|
+
- Also update the `columnKey` in `applyColumnFilter` function in TypeScript
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## Summary
|
|
679
|
+
|
|
680
|
+
✅ **What it does**: Provides advanced column filtering for tables/lists
|
|
681
|
+
✅ **Data adaptation**: Simply update `columnKey` and `columnName` in HTML
|
|
682
|
+
✅ **Match All Rules**: Feature to combine multiple rules with AND/OR logic
|
|
683
|
+
✅ **Easy to Use**: Simple API, fully customizable
|
|
684
|
+
✅ **Multiple Field Types**: Support for text, currency, age, date, and status fields
|
|
685
|
+
✅ **Programmatic Control**: Clear filters programmatically
|
|
686
|
+
✅ **Better UX**: Only one filter open at a time, ESC key support
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
**Made with ❤️ by Shivam Sharma**
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Shivam Sharma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|