ngx-column-filter-popup 1.0.10 → 1.0.11

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/README.md CHANGED
@@ -1,892 +1,63 @@
1
- # Column Filter Library
1
+ # ColumnFilterPopup
2
2
 
3
- A powerful, reusable Angular column filter component with support for multiple field types, advanced filtering rules, and customizable match modes.
3
+ This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.0.
4
4
 
5
- [![npm version](https://badge.fury.io/js/ngx-column-filter-popup.svg)](https://www.npmjs.com/package/ngx-column-filter-popup)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![GitHub stars](https://img.shields.io/github/stars/kakarotx10/ngx-column-filter.svg?style=social&label=Star)](https://github.com/kakarotx10/ngx-column-filter)
8
- [![GitHub forks](https://img.shields.io/github/forks/kakarotx10/ngx-column-filter.svg?style=social&label=Fork)](https://github.com/kakarotx10/ngx-column-filter)
5
+ ## Code scaffolding
9
6
 
10
- ## 🚀 Quick Links
11
-
12
- - 🎯 [Live Demo](https://ngx-column-filter.netlify.app/) - See it in action!
13
- - 📦 [NPM Package](https://www.npmjs.com/package/ngx-column-filter-popup)
14
- - 🏠 [GitHub Repository](https://github.com/kakarotx10/ngx-column-filter)
15
-
16
- ## 📚 Documentation & Guides
17
-
18
- **New to this library?** Start here:
19
-
20
- - 📖 **[Getting Started Tutorial](./GETTING_STARTED.md)** ⭐ - **Complete step-by-step guide: How to install, import, and use**
21
- - Installation steps
22
- - TypeScript imports explained
23
- - Basic and advanced examples
24
- - All field types with code samples
25
- - Data filtering implementation
26
- - Troubleshooting guide
27
-
28
- **More Resources:**
29
-
30
- - 📘 [Complete Documentation](./DOCUMENTATION.md) - Full API reference, all features explained
31
- - 💡 [Usage Examples](./USAGE_EXAMPLES.md) - Advanced usage patterns and programmatic control
32
- - 🚀 [Deployment Guide](./DEPLOYMENT.md) - How to deploy your Angular app
33
-
34
- ## 🐛 Support
35
-
36
- - 🐛 [Report Bug](https://github.com/kakarotx10/ngx-column-filter/issues)
37
- - 💬 [Request Feature](https://github.com/kakarotx10/ngx-column-filter/issues)
38
-
39
- ## Features
40
-
41
- - ✅ **Multiple Filter Rules**: Add multiple filter conditions per column
42
- - ✅ **Multiple Field Types**: Specialized filters for different data types
43
- - **Text** - Text fields (default)
44
- - **Currency** - Currency values with symbol support
45
- - **Age/Number** - Numeric values
46
- - **Date** - Date values with date picker
47
- - **Status** - Predefined status options dropdown
48
- - ✅ **Global Match Mode**: Choose how to combine multiple rules
49
- - **Match All Rules** (AND Logic): All rules must match
50
- - **Match Any Rule** (OR Logic): Any rule can match (default)
51
- - ✅ **Various Match Types**: Different matching options based on field type
52
- - ✅ **Visual Feedback**: Blinking red filter icon with X mark when active - clearly indicates applied filters
53
- - ✅ **Backend Mode**: Send filter payloads directly to your backend API instead of filtering locally
54
- - ✅ **Single/Multiple Rules**: Control whether users can add multiple filter rules with `allowMultipleRules` option
55
- - ✅ **Single Filter Open**: Only one filter dropdown can be open at a time
56
- - ✅ **ESC Key Support**: Press ESC to close the open filter
57
- - ✅ **Programmatic Control**: Clear filters programmatically using `clearFilter()` method
58
- - ✅ **Type-Safe**: Fully typed with TypeScript
59
- - ✅ **Standalone Component**: Works with Angular 14+ standalone components
60
- - ✅ **Fully Customizable**: Configurable inputs for customization
61
- - ✅ **Accessible**: ARIA labels and keyboard navigation support
62
- - ✅ **Data Adaptive**: Simply update `columnKey` and `columnName` when your data changes
63
-
64
- ## Installation
7
+ Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
65
8
 
66
9
  ```bash
67
- npm install ngx-column-filter-popup
68
- ```
69
-
70
- ### TypeScript Configuration
71
-
72
- This package is published with TypeScript source files. To avoid compilation errors, add this to your `tsconfig.json`:
73
-
74
- ```json
75
- {
76
- "compilerOptions": {
77
- "skipLibCheck": true
78
- }
79
- }
80
- ```
81
-
82
- **Or** if you're using Angular's default tsconfig, ensure `skipLibCheck` is enabled (it's usually enabled by default in Angular 14+).
83
-
84
- > 💡 **New to this library?** Check out the **[Getting Started Tutorial](./GETTING_STARTED.md)** for a complete step-by-step guide with examples!
85
-
86
- ## Quick Start
87
-
88
- ### Standalone Component (Angular 14+)
89
-
90
- #### Basic Example:
91
-
92
- ```typescript
93
- import { Component } from '@angular/core';
94
- import { ColumnFilterComponent } from 'ngx-column-filter-popup';
95
- import { FilterConfig, applyColumnFilter } from 'ngx-column-filter-popup';
96
-
97
- @Component({
98
- selector: 'app-example',
99
- imports: [ColumnFilterComponent],
100
- template: `
101
- <lib-column-filter
102
- columnName="first name"
103
- columnKey="firstName"
104
- (filterApplied)="onFilterApplied($event)"
105
- (filterCleared)="onFilterCleared()">
106
- </lib-column-filter>
107
- `
108
- })
109
- export class ExampleComponent {
110
- onFilterApplied(filterConfig: FilterConfig) {
111
- console.log('Filter applied:', filterConfig);
112
- // Apply filter to your data
113
- }
114
-
115
- onFilterCleared() {
116
- console.log('Filter cleared');
117
- // Clear filter from your data
118
- }
119
- }
120
- ```
121
-
122
- #### Complete Example with New Features (Backend Mode, allowMultipleRules):
123
-
124
- ```typescript
125
- import { Component, ViewChildren, QueryList } from '@angular/core';
126
- import { CommonModule } from '@angular/common';
127
- import { ColumnFilterComponent } from 'ngx-column-filter-popup';
128
- import { FilterConfig, applyColumnFilter } from 'ngx-column-filter-popup';
129
-
130
- interface User {
131
- id: number;
132
- firstName: string;
133
- lastName: string;
134
- email: string;
135
- }
136
-
137
- @Component({
138
- selector: 'app-example',
139
- imports: [CommonModule, ColumnFilterComponent],
140
- template: `
141
- <table>
142
- <thead>
143
- <tr>
144
- <th>
145
- First Name
146
- <lib-column-filter
147
- columnName="first name"
148
- columnKey="firstName"
149
- [allowMultipleRules]="false"
150
- [backendMode]="isBackendMode('firstName')"
151
- (filterApplied)="onFilterApplied('firstName', $event)"
152
- (filterCleared)="onFilterCleared('firstName')">
153
- </lib-column-filter>
154
- </th>
155
- <th>
156
- Last Name
157
- <lib-column-filter
158
- columnName="last name"
159
- columnKey="lastName"
160
- [allowMultipleRules]="true"
161
- [backendMode]="isBackendMode('lastName')"
162
- (filterApplied)="onFilterApplied('lastName', $event)"
163
- (filterCleared)="onFilterCleared('lastName')">
164
- </lib-column-filter>
165
- </th>
166
- <th>
167
- Email
168
- <lib-column-filter
169
- columnName="email"
170
- columnKey="email"
171
- [backendMode]="isBackendMode('email')"
172
- (filterApplied)="onFilterApplied('email', $event)"
173
- (filterCleared)="onFilterCleared('email')">
174
- </lib-column-filter>
175
- </th>
176
- </tr>
177
- </thead>
178
- <tbody>
179
- <tr *ngFor="let user of filteredUsers">
180
- <td>{{ user.firstName }}</td>
181
- <td>{{ user.lastName }}</td>
182
- <td>{{ user.email }}</td>
183
- </tr>
184
- </tbody>
185
- </table>
186
- <button (click)="clearAllFilters()">Clear All Filters</button>
187
- `
188
- })
189
- export class ExampleComponent {
190
- originalData: User[] = [
191
- { id: 1, firstName: 'John', lastName: 'Doe', email: 'john@example.com' }
192
- ];
193
- filteredData: User[] = [...this.originalData];
194
-
195
- // ✅ Unified filter storage - single source of truth
196
- filters = new Map<string, FilterConfig | null>();
197
-
198
- // ✅ Configuration: Which columns use backend mode
199
- readonly backendModeColumns = new Set<string>(['firstName', 'email']);
200
-
201
- @ViewChildren(ColumnFilterComponent) filterComponents!: QueryList<ColumnFilterComponent>;
202
-
203
- // ✅ Generic filter handler - works for all columns
204
- onFilterApplied(columnKey: string, filterConfig: FilterConfig) {
205
- this.filters.set(columnKey, filterConfig);
206
-
207
- if (this.isBackendMode(columnKey)) {
208
- this.sendAllBackendFiltersToBackend();
209
- }
210
-
211
- this.applyAllFilters();
212
- }
213
-
214
- // ✅ Generic filter clear handler
215
- onFilterCleared(columnKey: string) {
216
- this.filters.set(columnKey, null);
217
-
218
- if (this.isBackendMode(columnKey)) {
219
- this.sendAllBackendFiltersToBackend();
220
- }
221
-
222
- this.applyAllFilters();
223
- }
224
-
225
- // ✅ Check if column uses backend mode
226
- isBackendMode(columnKey: string): boolean {
227
- return this.backendModeColumns.has(columnKey);
228
- }
229
-
230
- // ✅ Apply all filters - automatically skips backend mode columns
231
- private applyAllFilters() {
232
- let result = [...this.originalData];
233
-
234
- this.filters.forEach((filterConfig, columnKey) => {
235
- // Skip backend mode columns (handled by backend)
236
- if (filterConfig && !this.isBackendMode(columnKey)) {
237
- result = applyColumnFilter(result, columnKey, filterConfig);
238
- }
239
- });
240
-
241
- this.filteredData = result;
242
- }
243
-
244
- // ✅ Clear all filters programmatically
245
- clearAllFilters() {
246
- this.filters.clear();
247
- this.sendAllBackendFiltersToBackend();
248
- this.filteredData = [...this.originalData];
249
-
250
- // Clear UI state in all filter components (icons/inputs)
251
- if (this.filterComponents) {
252
- this.filterComponents.forEach((filter: ColumnFilterComponent) => {
253
- filter.clearFilter();
254
- });
255
- }
256
- }
257
-
258
- // ✅ Send all backend filters to API
259
- private sendAllBackendFiltersToBackend() {
260
- const activeFilters: Array<{
261
- field: string;
262
- matchType: string;
263
- value: string;
264
- fieldType: string;
265
- }> = [];
266
-
267
- this.backendModeColumns.forEach(columnKey => {
268
- const filterConfig = this.filters.get(columnKey);
269
- if (filterConfig && filterConfig.rules.length > 0) {
270
- filterConfig.rules.forEach(rule => {
271
- if (rule.value && rule.value.trim() !== '') {
272
- activeFilters.push({
273
- field: columnKey,
274
- matchType: rule.matchType,
275
- value: rule.value.trim(),
276
- fieldType: filterConfig.fieldType || 'text'
277
- });
278
- }
279
- });
280
- }
281
- });
282
-
283
- const payload = { activeFilters, count: activeFilters.length };
284
- // Send to your backend API
285
- console.log('Backend payload:', payload);
286
- }
287
- }
288
- ```
289
-
290
- ### Module-Based (Optional)
291
-
292
- ```typescript
293
- import { NgModule } from '@angular/core';
294
- import { ColumnFilterModule } from 'ngx-column-filter-popup';
295
-
296
- @NgModule({
297
- imports: [ColumnFilterModule],
298
- // ...
299
- })
300
- export class YourModule {}
301
- ```
302
-
303
- ## Field Types
304
-
305
- ### Text Field (Default)
306
-
307
- ```html
308
- <lib-column-filter
309
- columnName="name"
310
- columnKey="name"
311
- fieldType="text"
312
- (filterApplied)="onFilterApplied('name', $event)"
313
- (filterCleared)="onFilterCleared('name')">
314
- </lib-column-filter>
315
- ```
316
-
317
- ### Currency Field
318
-
319
- ```html
320
- <lib-column-filter
321
- columnName="balance"
322
- columnKey="balance"
323
- fieldType="currency"
324
- currencySymbol="$"
325
- (filterApplied)="onFilterApplied('balance', $event)"
326
- (filterCleared)="onFilterCleared('balance')">
327
- </lib-column-filter>
328
- ```
329
-
330
- ### Age/Number Field
331
-
332
- ```html
333
- <lib-column-filter
334
- columnName="age"
335
- columnKey="age"
336
- fieldType="age"
337
- (filterApplied)="onFilterApplied('age', $event)"
338
- (filterCleared)="onFilterCleared('age')">
339
- </lib-column-filter>
340
- ```
341
-
342
- ### Date Field
343
-
344
- ```html
345
- <lib-column-filter
346
- columnName="date"
347
- columnKey="date"
348
- fieldType="date"
349
- (filterApplied)="onFilterApplied('date', $event)"
350
- (filterCleared)="onFilterCleared('date')">
351
- </lib-column-filter>
352
- ```
353
-
354
- ### Status Field
355
-
356
- ```html
357
- <lib-column-filter
358
- columnName="status"
359
- columnKey="status"
360
- fieldType="status"
361
- [statusOptions]="['qualified', 'unqualified', 'negotiation', 'new']"
362
- (filterApplied)="onFilterApplied('status', $event)"
363
- (filterCleared)="onFilterCleared('status')">
364
- </lib-column-filter>
365
- ```
366
-
367
- > 💡 **Note**: All examples use generic handlers `onFilterApplied(columnKey, $event)` and `onFilterCleared(columnKey)` - no need for separate functions per filter!
368
-
369
- ## API Reference
370
-
371
- ### ColumnFilterComponent
372
-
373
- #### Inputs
374
-
375
- | Input | Type | Default | Description |
376
- |-------|------|---------|-------------|
377
- | `columnName` | `string` | `''` | Display name of the column (used in placeholder) |
378
- | `columnKey` | `string` | `''` | Property name to filter on |
379
- | `fieldType` | `FieldType` | `'text'` | Field type: 'text', 'currency', 'age', 'date', or 'status' |
380
- | `currencySymbol` | `string` | `'$'` | Currency symbol for currency field type (optional) |
381
- | `statusOptions` | `string[]` | `[]` | Array of status options for status field type (required for status) |
382
- | `initialFilter` | `FilterConfig?` | `undefined` | Initial filter configuration (optional) |
383
- | `placeholder` | `string?` | `undefined` | Custom placeholder text. Default: "Search by {columnName}" |
384
- | `availableMatchTypes` | `MatchType[]?` | `undefined` | Customize available match types (optional) |
385
- | `backendMode` | `boolean` | `false` | When true, component emits filter data for backend API instead of frontend filtering |
386
- | `allowMultipleRules` | `boolean` | `true` | When false, hides Add/Remove Rule buttons (single rule only) |
387
-
388
- #### Outputs
389
-
390
- | Output | Type | Description |
391
- |--------|------|-------------|
392
- | `filterApplied` | `EventEmitter<FilterConfig>` | Emitted when filter is applied |
393
- | `filterCleared` | `EventEmitter<void>` | Emitted when filter is cleared |
394
-
395
- #### Public Methods
396
-
397
- | Method | Description |
398
- |--------|-------------|
399
- | `clearFilter()` | Programmatically clear all filter rules and reset the filter |
400
-
401
- ### FilterConfig Interface
402
-
403
- ```typescript
404
- type FieldType = 'text' | 'currency' | 'age' | 'date' | 'status';
405
-
406
- interface FilterConfig {
407
- rules: FilterRule[];
408
- globalMatchMode?: GlobalMatchMode; // 'match-all-rules' | 'match-any-rule'
409
- fieldType?: FieldType;
410
- statusOptions?: string[];
411
- }
412
-
413
- interface FilterRule {
414
- id: string;
415
- matchType: MatchType;
416
- value: string;
417
- }
10
+ ng generate component component-name
418
11
  ```
419
12
 
420
- ### Utility Functions
421
-
422
- #### applyColumnFilter
423
-
424
- Apply filter rules to a dataset:
425
-
426
- ```typescript
427
- import { applyColumnFilter } from 'ngx-column-filter-popup';
428
-
429
- const filteredData = applyColumnFilter(
430
- data,
431
- 'columnKey',
432
- filterConfig
433
- );
434
- ```
435
-
436
- #### itemMatchesFilter
437
-
438
- Check if a single item matches filter rules:
439
-
440
- ```typescript
441
- import { itemMatchesFilter } from 'ngx-column-filter-popup';
13
+ For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
442
14
 
443
- const matches = itemMatchesFilter(
444
- item,
445
- 'columnKey',
446
- filterConfig
447
- );
15
+ ```bash
16
+ ng generate --help
448
17
  ```
449
18
 
450
- ## Backend Mode (Backend API Integration)
451
-
452
- When `backendMode` is enabled, the component collects filter data and emits it in a format ready for your backend API. No frontend filtering is applied.
453
-
454
- ### Backend Mode Example:
455
-
456
- ```typescript
457
- import { Component } from '@angular/core';
458
- import { ColumnFilterComponent } from 'ngx-column-filter-popup';
459
- import { FilterConfig } from 'ngx-column-filter-popup';
460
-
461
- @Component({
462
- selector: 'app-example',
463
- imports: [ColumnFilterComponent],
464
- template: `
465
- <lib-column-filter
466
- columnName="first name"
467
- columnKey="firstName"
468
- [backendMode]="true"
469
- (filterApplied)="onFilterApplied($event)"
470
- (filterCleared)="onFilterCleared()">
471
- </lib-column-filter>
472
- `
473
- })
474
- export class ExampleComponent {
475
- filters = new Map<string, FilterConfig | null>();
476
- readonly backendModeColumns = new Set<string>(['firstName', 'email']);
477
-
478
- onFilterApplied(columnKey: string, filterConfig: FilterConfig) {
479
- this.filters.set(columnKey, filterConfig);
480
- this.sendToBackend();
481
- }
482
-
483
- onFilterCleared(columnKey: string) {
484
- this.filters.set(columnKey, null);
485
- this.sendToBackend();
486
- }
487
-
488
- private sendToBackend() {
489
- const activeFilters: Array<{
490
- field: string;
491
- matchType: string;
492
- value: string;
493
- fieldType: string;
494
- }> = [];
495
-
496
- this.backendModeColumns.forEach(columnKey => {
497
- const filterConfig = this.filters.get(columnKey);
498
- if (filterConfig && filterConfig.rules.length > 0) {
499
- filterConfig.rules.forEach(rule => {
500
- if (rule.value && rule.value.trim() !== '') {
501
- activeFilters.push({
502
- field: columnKey,
503
- matchType: rule.matchType,
504
- value: rule.value.trim(),
505
- fieldType: filterConfig.fieldType || 'text'
506
- });
507
- }
508
- });
509
- }
510
- });
19
+ ## Building
511
20
 
512
- const payload = {
513
- activeFilters: activeFilters,
514
- count: activeFilters.length
515
- };
21
+ To build the library, run:
516
22
 
517
- // Send to your backend API
518
- // this.httpClient.post('/api/filters', payload).subscribe(...);
519
- console.log('Backend payload:', payload);
520
- }
521
- }
522
- ```
523
-
524
- ### Backend Payload Format:
525
-
526
- ```json
527
- {
528
- "activeFilters": [
529
- {
530
- "field": "firstName",
531
- "matchType": "contains",
532
- "value": "John",
533
- "fieldType": "text"
534
- },
535
- {
536
- "field": "email",
537
- "matchType": "contains",
538
- "value": "example",
539
- "fieldType": "text"
540
- }
541
- ],
542
- "count": 2
543
- }
23
+ ```bash
24
+ ng build column-filter-popup
544
25
  ```
545
26
 
546
- ## Single Rule Mode (allowMultipleRules)
547
-
548
- Control whether users can add multiple filter rules:
549
-
550
- ```html
551
- <!-- Multiple rules allowed (default) -->
552
- <lib-column-filter
553
- columnName="name"
554
- columnKey="name"
555
- [allowMultipleRules]="true">
556
- </lib-column-filter>
557
-
558
- <!-- Single rule only (Add/Remove buttons hidden) -->
559
- <lib-column-filter
560
- columnName="email"
561
- columnKey="email"
562
- [allowMultipleRules]="false">
563
- </lib-column-filter>
564
- ```
27
+ This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
565
28
 
566
- **When `allowMultipleRules="false"`:**
567
- - ✅ Add Rule button is hidden
568
- - ✅ Remove Rule buttons are hidden
569
- - ✅ Global Match Mode toggle is hidden
570
- - ✅ Users can only use a single filter rule
29
+ ### Publishing the Library
571
30
 
572
- **When `allowMultipleRules="true"` (default):**
573
- - ✅ All features work normally
574
- - ✅ Users can add multiple rules
575
- - ✅ Match All/Match Any toggle is available
31
+ Once the project is built, you can publish your library by following these steps:
576
32
 
577
- ## Programmatic Control
33
+ 1. Navigate to the `dist` directory:
34
+ ```bash
35
+ cd dist/column-filter-popup
36
+ ```
578
37
 
579
- ### Clearing Filters Programmatically
38
+ 2. Run the `npm publish` command to publish your library to the npm registry:
39
+ ```bash
40
+ npm publish
41
+ ```
580
42
 
581
- ```typescript
582
- import { Component, ViewChild } from '@angular/core';
583
- import { ColumnFilterComponent } from 'ngx-column-filter-popup';
43
+ ## Running unit tests
584
44
 
585
- @Component({
586
- template: `
587
- <lib-column-filter
588
- #nameFilter
589
- columnName="name"
590
- columnKey="name">
591
- </lib-column-filter>
592
- <button (click)="clearFilter()">Clear Filter</button>
593
- `
594
- })
595
- export class ExampleComponent {
596
- @ViewChild('nameFilter') filter!: ColumnFilterComponent;
45
+ To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
597
46
 
598
- clearFilter() {
599
- this.filter.clearFilter(); // Programmatically clear the filter
600
- }
601
- }
47
+ ```bash
48
+ ng test
602
49
  ```
603
50
 
604
- ## Complete Example
605
-
606
- **✅ Modern Implementation using Generic Handlers (Recommended):**
607
-
608
- ```typescript
609
- import { Component, ViewChildren, QueryList } from '@angular/core';
610
- import { CommonModule } from '@angular/common';
611
- import { ColumnFilterComponent } from 'ngx-column-filter-popup';
612
- import { FilterConfig, applyColumnFilter } from 'ngx-column-filter-popup';
613
-
614
- interface User {
615
- id: number;
616
- firstName: string;
617
- lastName: string;
618
- email: string;
619
- age: number;
620
- balance: number;
621
- joinDate: string;
622
- status: string;
623
- }
624
-
625
- @Component({
626
- selector: 'app-user-list',
627
- imports: [CommonModule, ColumnFilterComponent],
628
- template: `
629
- <button (click)="clearAllFilters()">Clear All Filters</button>
630
- <table>
631
- <thead>
632
- <tr>
633
- <th>
634
- First Name
635
- <lib-column-filter
636
- columnName="first name"
637
- columnKey="firstName"
638
- [allowMultipleRules]="false"
639
- [backendMode]="isBackendMode('firstName')"
640
- (filterApplied)="onFilterApplied('firstName', $event)"
641
- (filterCleared)="onFilterCleared('firstName')">
642
- </lib-column-filter>
643
- </th>
644
- <th>
645
- Last Name
646
- <lib-column-filter
647
- columnName="last name"
648
- columnKey="lastName"
649
- (filterApplied)="onFilterApplied('lastName', $event)"
650
- (filterCleared)="onFilterCleared('lastName')">
651
- </lib-column-filter>
652
- </th>
653
- <th>
654
- Email
655
- <lib-column-filter
656
- columnName="email"
657
- columnKey="email"
658
- [backendMode]="isBackendMode('email')"
659
- (filterApplied)="onFilterApplied('email', $event)"
660
- (filterCleared)="onFilterCleared('email')">
661
- </lib-column-filter>
662
- </th>
663
- <th>
664
- Age
665
- <lib-column-filter
666
- columnName="age"
667
- columnKey="age"
668
- fieldType="age"
669
- (filterApplied)="onFilterApplied('age', $event)"
670
- (filterCleared)="onFilterCleared('age')">
671
- </lib-column-filter>
672
- </th>
673
- <th>
674
- Balance
675
- <lib-column-filter
676
- columnName="balance"
677
- columnKey="balance"
678
- fieldType="currency"
679
- currencySymbol="$"
680
- (filterApplied)="onFilterApplied('balance', $event)"
681
- (filterCleared)="onFilterCleared('balance')">
682
- </lib-column-filter>
683
- </th>
684
- <th>
685
- Join Date
686
- <lib-column-filter
687
- columnName="join date"
688
- columnKey="joinDate"
689
- fieldType="date"
690
- (filterApplied)="onFilterApplied('joinDate', $event)"
691
- (filterCleared)="onFilterCleared('joinDate')">
692
- </lib-column-filter>
693
- </th>
694
- <th>
695
- Status
696
- <lib-column-filter
697
- columnName="status"
698
- columnKey="status"
699
- fieldType="status"
700
- [statusOptions]="statusOptions"
701
- (filterApplied)="onFilterApplied('status', $event)"
702
- (filterCleared)="onFilterCleared('status')">
703
- </lib-column-filter>
704
- </th>
705
- </tr>
706
- </thead>
707
- <tbody>
708
- <tr *ngFor="let user of filteredUsers">
709
- <td>{{ user.firstName }}</td>
710
- <td>{{ user.lastName }}</td>
711
- <td>{{ user.email }}</td>
712
- <td>{{ user.age }}</td>
713
- <td>${{ user.balance }}</td>
714
- <td>{{ user.joinDate }}</td>
715
- <td>{{ user.status }}</td>
716
- </tr>
717
- </tbody>
718
- </table>
719
- `
720
- })
721
- export class UserListComponent {
722
- users: User[] = [
723
- { id: 1, firstName: 'John', lastName: 'Doe', email: 'john@example.com', age: 30, balance: 50000, joinDate: '2020-01-15', status: 'active' }
724
- ];
725
-
726
- filteredUsers: User[] = [...this.users];
727
- statusOptions = ['active', 'inactive', 'on-leave'];
728
-
729
- // ✅ Unified filter storage - single source of truth
730
- filters = new Map<string, FilterConfig | null>();
731
-
732
- // ✅ Configuration: Which columns use backend mode
733
- readonly backendModeColumns = new Set<string>(['firstName', 'email']);
734
-
735
- @ViewChildren(ColumnFilterComponent) filterComponents!: QueryList<ColumnFilterComponent>;
736
-
737
- // ✅ Generic filter handler - works for ALL columns (no separate functions needed!)
738
- onFilterApplied(columnKey: string, filterConfig: FilterConfig): void {
739
- this.filters.set(columnKey, filterConfig);
740
-
741
- if (this.isBackendMode(columnKey)) {
742
- this.sendAllBackendFiltersToBackend();
743
- }
744
-
745
- this.applyAllFilters();
746
- }
747
-
748
- // ✅ Generic filter clear handler - works for ALL columns
749
- onFilterCleared(columnKey: string): void {
750
- this.filters.set(columnKey, null);
751
-
752
- if (this.isBackendMode(columnKey)) {
753
- this.sendAllBackendFiltersToBackend();
754
- }
755
-
756
- this.applyAllFilters();
757
- }
758
-
759
- // ✅ Check if column uses backend mode
760
- isBackendMode(columnKey: string): boolean {
761
- return this.backendModeColumns.has(columnKey);
762
- }
763
-
764
- // ✅ Apply all filters - automatically skips backend mode columns
765
- private applyAllFilters(): void {
766
- let result = [...this.users];
767
-
768
- this.filters.forEach((filterConfig, columnKey) => {
769
- // Skip backend mode columns (handled by backend)
770
- if (filterConfig && !this.isBackendMode(columnKey)) {
771
- result = applyColumnFilter(result, columnKey, filterConfig);
772
- }
773
- });
774
-
775
- this.filteredUsers = result;
776
- }
777
-
778
- // ✅ Clear all filters programmatically
779
- clearAllFilters(): void {
780
- this.filters.clear();
781
- this.sendAllBackendFiltersToBackend();
782
- this.filteredUsers = [...this.users];
783
-
784
- // Clear UI state in all filter components (icons/inputs)
785
- if (this.filterComponents) {
786
- this.filterComponents.forEach((filter: ColumnFilterComponent) => {
787
- filter.clearFilter();
788
- });
789
- }
790
- }
791
-
792
- // ✅ Send all backend filters to API
793
- private sendAllBackendFiltersToBackend(): void {
794
- const activeFilters: Array<{
795
- field: string;
796
- matchType: string;
797
- value: string;
798
- fieldType: string;
799
- }> = [];
51
+ ## Running end-to-end tests
800
52
 
801
- this.backendModeColumns.forEach(columnKey => {
802
- const filterConfig = this.filters.get(columnKey);
803
- if (filterConfig && filterConfig.rules.length > 0) {
804
- filterConfig.rules.forEach(rule => {
805
- if (rule.value && rule.value.trim() !== '') {
806
- activeFilters.push({
807
- field: columnKey,
808
- matchType: rule.matchType,
809
- value: rule.value.trim(),
810
- fieldType: filterConfig.fieldType || 'text'
811
- });
812
- }
813
- });
814
- }
815
- });
53
+ For end-to-end (e2e) testing, run:
816
54
 
817
- const payload = { activeFilters, count: activeFilters.length };
818
- // Send to your backend API
819
- console.log('Backend payload:', payload);
820
- }
821
- }
55
+ ```bash
56
+ ng e2e
822
57
  ```
823
58
 
824
- **✨ Key Benefits of This Approach:**
825
- - ✅ **No separate functions per filter** - One `onFilterApplied()` handles all columns
826
- - ✅ **Easy to add new filters** - Just add HTML, no new functions needed
827
- - ✅ **Clean and maintainable** - Map-based storage, generic handlers
828
- - ✅ **Backend mode support** - Configurable per column
829
- - ✅ **Single source of truth** - All filters in one Map
830
-
831
- ## 📖 Documentation
832
-
833
- ### For Beginners:
834
- - **[Getting Started Tutorial](./GETTING_STARTED.md)** - Step-by-step guide with examples
835
- - What to import in TypeScript
836
- - How to setup component
837
- - Complete working examples
838
- - Common patterns
839
-
840
- ### For Advanced Users:
841
- - **[Complete Documentation](./DOCUMENTATION.md)** - Full API reference
842
- - All inputs and outputs
843
- - Utility functions
844
- - Type definitions
845
- - Match modes explained
846
- - Data structure adaptation
847
-
848
- - **[Usage Examples](./USAGE_EXAMPLES.md)** - Advanced patterns
849
- - Programmatic filter control
850
- - Multiple filters management
851
- - Custom configurations
852
-
853
- - **[Deployment Guide](./DEPLOYMENT.md)** - Deploy your Angular app
854
- - GitHub Pages
855
- - Vercel
856
- - Netlify
857
- - Firebase Hosting
858
-
859
- ## Styling
860
-
861
- The component uses SCSS and includes default styles. You can customize the appearance by overriding CSS classes:
862
-
863
- - `.column-filter-wrapper` - Main wrapper
864
- - `.filter-trigger` - Filter button
865
- - `.filter-dropdown` - Dropdown container
866
- - `.filter-rule` - Individual filter rule
867
- - `.btn-apply` - Apply button
868
- - `.btn-clear` - Clear button
869
-
870
- ## Browser Support
871
-
872
- - Chrome (latest)
873
- - Firefox (latest)
874
- - Safari (latest)
875
- - Edge (latest)
876
-
877
- ## Requirements
878
-
879
- - Angular 14+
880
- - TypeScript 4.7+
881
-
882
- ## License
883
-
884
- MIT
885
-
886
- ## Author
887
-
888
- Made with ❤️ by Shivam Sharma
59
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
889
60
 
890
- ## Contributing
61
+ ## Additional Resources
891
62
 
892
- Contributions are welcome! Please feel free to submit a Pull Request.
63
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.