Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 3536263

Browse files
authored
Merge pull request #131 from ghiscoding/feat/filter-with-angular-component
feat(filter): Custom Filter with Angular Component
2 parents 6f28ca8 + 7b2f2d3 commit 3536263

19 files changed

+262
-44
lines changed

src/app/app.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
<a [routerLink]="['/rowdetail']">21- Row Detail View</a>
9393
</li>
9494
<li routerLinkActive="active">
95-
<a [routerLink]="['/angular-components']">22- Editors Angular Components</a>
95+
<a [routerLink]="['/angular-components']">22- Use of Angular Components</a>
9696
</li>
9797
</ul>
9898
</div>

src/app/app.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
1111
import { AppComponent } from './app.component';
1212
import { CustomTitleFormatterComponent } from './examples/custom-titleFormatter.component';
1313
import { EditorNgSelectComponent } from './examples/editor-ng-select.component';
14+
import { FilterNgSelectComponent } from './examples/filter-ng-select.component';
1415
import { GridAddItemComponent } from './examples/grid-additem.component';
1516
import { GridBasicComponent } from './examples/grid-basic.component';
1617
import { GridClientSideComponent } from './examples/grid-clientside.component';
@@ -74,6 +75,7 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
7475
AppComponent,
7576
CustomTitleFormatterComponent,
7677
EditorNgSelectComponent,
78+
FilterNgSelectComponent,
7779
GridAddItemComponent,
7880
GridAngularComponent,
7981
GridBasicComponent,
@@ -129,6 +131,7 @@ export function appInitializerFactory(translate: TranslateService, injector: Inj
129131
// dynamically created components
130132
CustomTitleFormatterComponent,
131133
EditorNgSelectComponent,
134+
FilterNgSelectComponent,
132135
RowDetailPreloadComponent,
133136
RowDetailViewComponent,
134137
],

src/app/examples/custom-angularComponentEditor.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ComponentRef } from '@angular/core';
2+
import { Subscription } from 'rxjs';
23
import {
34
AngularUtilService,
45
Column,
@@ -13,6 +14,8 @@ import {
1314
* KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.
1415
*/
1516
export class CustomAngularComponentEditor implements Editor {
17+
changeSubscriber: Subscription;
18+
1619
/** Angular Component Reference */
1720
componentRef: ComponentRef<any>;
1821

@@ -41,7 +44,7 @@ export class CustomAngularComponentEditor implements Editor {
4144

4245
/** Get the Collection */
4346
get collection(): any[] {
44-
return this.columnDef && this.columnDef && this.columnDef.internalColumnEditor.collection || [];
47+
return this.columnDef && this.columnDef.internalColumnEditor.collection || [];
4548
}
4649

4750
/** Get Column Definition object */
@@ -70,17 +73,21 @@ export class CustomAngularComponentEditor implements Editor {
7073

7174
init() {
7275
if (!this.columnEditor || !this.columnEditor.params.component || !(this.angularUtilService instanceof AngularUtilService)) {
73-
throw new Error(`[Angular-Slickgrid] For the Editors.angularComponent to work properly, you need to provide your component to the "component" property and make sure to add it to your "entryComponents" array.
76+
throw new Error(`[Angular-Slickgrid] For Editor with Angular Component to work properly, you need to provide your component to the "component" property and make sure to add it to your "entryComponents" array.
7477
You also need to provide the "AngularUtilService" via the Editor Params OR the Grid Options Params
75-
Example: this.columnDefs = [{ id: 'title', field: 'title', editor: { model: CustomAngularComponentEditor, collection: [...] }, params: { component: MyComponent, angularUtilService: this.angularUtilService }];
78+
Example: this.columnDefs = [{ id: 'title', field: 'title', editor: { model: CustomAngularComponentEditor, collection: [...], params: { component: MyComponent, angularUtilService: this.angularUtilService }}];
7679
OR this.columnDefs = [{ id: 'title', field: 'title', editor: { model: CustomAngularComponentEditor, collection: [...] }]; this.gridOptions = { params: { angularUtilService: this.angularUtilService }}`);
7780
}
7881
if (this.columnEditor && this.columnEditor.params.component) {
7982
const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(this.columnEditor.params.component, this.args.container);
8083
this.componentRef = componentOutput && componentOutput.componentRef;
84+
85+
// here we override the collection object of the Angular Component
86+
// but technically you can pass any values you wish to your Component
8187
Object.assign(this.componentRef.instance, { collection: this.collection });
8288

83-
this.componentRef.instance.onModelChanged.subscribe((item) => {
89+
// when our model (item object) changes, we'll call a save of the slickgrid editor
90+
this.changeSubscriber = this.componentRef.instance.onItemChanged.subscribe((item) => {
8491
this.save();
8592
});
8693
}
@@ -105,29 +112,30 @@ export class CustomAngularComponentEditor implements Editor {
105112
}
106113
}
107114

115+
/** optional, implement a hide method on your Angular Component */
108116
hide() {
109-
// optional, implement a hide method on your Angular Component
110117
if (this.componentRef && this.componentRef.instance && typeof this.componentRef.instance.hide === 'function') {
111118
this.componentRef.instance.hide();
112119
}
113120
}
114121

122+
/** optional, implement a show method on your Angular Component */
115123
show() {
116-
// optional, implement a show method on your Angular Component
117124
if (this.componentRef && this.componentRef.instance && typeof this.componentRef.instance.show === 'function') {
118125
this.componentRef.instance.show();
119126
}
120127
}
121128

129+
/** destroy the Angular Component & Subscription */
122130
destroy() {
123-
// destroy the Angular Component
124131
if (this.componentRef && this.componentRef.destroy) {
125132
this.componentRef.destroy();
133+
this.changeSubscriber.unsubscribe();
126134
}
127135
}
128136

137+
/** optional, implement a focus method on your Angular Component */
129138
focus() {
130-
// optional, implement a focus method on your Angular Component
131139
if (this.componentRef && this.componentRef.instance && typeof this.componentRef.instance.focus === 'function') {
132140
this.componentRef.instance.focus();
133141
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { ComponentRef } from '@angular/core';
2+
import { Subscription } from 'rxjs';
3+
import {
4+
AngularUtilService,
5+
Column,
6+
ColumnFilter,
7+
Filter,
8+
FilterArguments,
9+
FilterCallback,
10+
GridOption,
11+
OperatorType,
12+
OperatorString,
13+
SearchTerm,
14+
} from './../modules/angular-slickgrid';
15+
16+
// using external non-typed js libraries
17+
declare var $: any;
18+
19+
export class CustomAngularComponentFilter implements Filter {
20+
changeSubscriber: Subscription;
21+
22+
/** Angular Component Reference */
23+
componentRef: ComponentRef<any>;
24+
25+
grid: any;
26+
searchTerms: SearchTerm[];
27+
columnDef: Column;
28+
callback: FilterCallback;
29+
operator: OperatorType | OperatorString = OperatorType.equal;
30+
31+
constructor() {}
32+
33+
/** Angular Util Service (could be inside the Grid Options Params or the Filter Params ) */
34+
get angularUtilService(): AngularUtilService {
35+
let angularUtilService = this.gridOptions && this.gridOptions.params && this.gridOptions.params.angularUtilService;
36+
if (!angularUtilService || !(angularUtilService instanceof AngularUtilService)) {
37+
angularUtilService = this.columnFilter && this.columnFilter.params && this.columnFilter.params.angularUtilService;
38+
}
39+
return angularUtilService;
40+
}
41+
42+
/** Get the Collection */
43+
get collection(): any[] {
44+
return this.columnFilter && this.columnFilter.collection || [];
45+
}
46+
47+
/** Getter for the Column Filter */
48+
get columnFilter(): ColumnFilter {
49+
return this.columnDef && this.columnDef.filter || {};
50+
}
51+
52+
/** Getter for the Grid Options pulled through the Grid Object */
53+
get gridOptions(): GridOption {
54+
return (this.grid && this.grid.getOptions) ? this.grid.getOptions() : {};
55+
}
56+
57+
/**
58+
* Initialize the Filter
59+
*/
60+
init(args: FilterArguments) {
61+
this.grid = args.grid;
62+
this.callback = args.callback;
63+
this.columnDef = args.columnDef;
64+
this.searchTerms = args.searchTerms || [];
65+
66+
if (!this.columnFilter || !this.columnFilter.params.component || !(this.angularUtilService instanceof AngularUtilService)) {
67+
throw new Error(`[Angular-Slickgrid] For Filter with Angular Component to work properly, you need to provide your component to the "component" property and make sure to add it to your "entryComponents" array.
68+
You also need to provide the "AngularUtilService" via the Filter Params OR the Grid Options Params
69+
Example: this.columnDefs = [{ id: 'title', field: 'title', filter: { model: CustomAngularComponentFilter, collection: [...], params: { component: MyComponent, angularUtilService: this.angularUtilService }}];
70+
OR this.columnDefs = [{ id: 'title', field: 'title', filter: { model: CustomAngularComponentFilter, collection: [...] }]; this.gridOptions = { params: { angularUtilService: this.angularUtilService }}`);
71+
}
72+
73+
if (this.columnFilter && this.columnFilter.params.component) {
74+
// use a delay to make sure Angular ran at least a full cycle and it finished rendering the Component before hooking onto it
75+
// else we get the infamous error "ExpressionChangedAfterItHasBeenCheckedError"
76+
setTimeout(() => {
77+
const $headerElm = this.grid.getHeaderRowColumn(this.columnDef.id);
78+
$($headerElm).empty();
79+
const componentOuput = this.angularUtilService.createAngularComponentAppendToDom(this.columnFilter.params.component, $headerElm);
80+
this.componentRef = componentOuput.componentRef;
81+
82+
// here we override the collection object of the Angular Component
83+
// but technically you can pass any values you wish to your Component
84+
Object.assign(componentOuput.componentRef.instance, { collection: this.collection });
85+
86+
this.changeSubscriber = componentOuput.componentRef.instance.onItemChanged.subscribe((item) => {
87+
this.callback(undefined, { columnDef: this.columnDef, operator: this.operator, searchTerms: [item.id] });
88+
});
89+
});
90+
}
91+
}
92+
93+
/**
94+
* Clear the filter value
95+
*/
96+
clear() {
97+
if (this.componentRef && this.componentRef.instance && this.componentRef.instance.hasOwnProperty('selectedId')) {
98+
this.componentRef.instance.selectedId = 0;
99+
}
100+
}
101+
102+
/** destroy the Angular Component & Subscription */
103+
destroy() {
104+
if (this.componentRef && this.componentRef.destroy) {
105+
this.componentRef.destroy();
106+
this.changeSubscriber.unsubscribe();
107+
}
108+
}
109+
110+
/**
111+
* Set value(s) on the DOM element
112+
*/
113+
setValues(values) {
114+
if (this.componentRef && this.componentRef.instance && this.componentRef.instance.hasOwnProperty('selectedId')) {
115+
this.componentRef.instance.selectedId = values;
116+
}
117+
}
118+
}

src/app/examples/custom-inputFilter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class CustomInputFilter implements Filter {
2424

2525
constructor() {}
2626

27-
/** Getter for the Filter Operator */
27+
/** Getter for the Column Filter */
2828
get columnFilter(): ColumnFilter {
2929
return this.columnDef && this.columnDef.filter || {};
3030
}

src/app/examples/editor-ng-select.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ export class EditorNgSelectComponent {
2121
selectedId: string;
2222
selectedItem: any;
2323
collection; // this will be filled by the collection of your column definition
24-
onModelChanged = new Subject<any>(); // object
24+
onItemChanged = new Subject<any>(); // object
2525

2626
onChange(item: any) {
2727
this.selectedItem = item;
28-
this.onModelChanged.next(item);
28+
this.onItemChanged.next(item);
2929
}
3030

3131
focus() {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Component } from '@angular/core';
2+
import { Subject } from 'rxjs';
3+
4+
// the appendTo="body" (necessary for SlickGrid filter) requires the body to be position relative like so
5+
// <body style="position: relative">
6+
@Component({
7+
template: `
8+
<ng-select class="custom no-style-select"
9+
[items]="collection"
10+
bindValue="id"
11+
bindLabel="name"
12+
[clearable]="false"
13+
appendTo="body"
14+
(change)="onChange($event)"
15+
[(ngModel)]="selectedId"
16+
>
17+
<ng-template ng-label-tmp ng-option-tmp let-item="item">
18+
{{ item?.name }}
19+
</ng-template>
20+
</ng-select>`
21+
})
22+
export class FilterNgSelectComponent {
23+
selectedId: string;
24+
selectedItem: any;
25+
collection; // this will be filled by the collection of your column definition
26+
onItemChanged = new Subject<any>(); // object
27+
28+
onChange(item: any) {
29+
this.selectedItem = item;
30+
this.onItemChanged.next(item);
31+
}
32+
}

src/app/examples/grid-angular.component.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ <h2>{{title}}</h2>
44
<br>
55

66
<div class="col-sm-6">
7-
<label>autoEdit setting</label>
7+
<label>autoEdit setting: </label>
88
<span id="radioAutoEdit">
99
<label class="radio-inline control-label" for="radioTrue">
1010
<input type="radio" name="inlineRadioOptions" id="radioTrue" checked [value]="isAutoEdit" (change)="setAutoEdit(true)"> ON (single-click)
@@ -19,10 +19,12 @@ <h2>{{title}}</h2>
1919
<i class="fa fa-undo"></i>
2020
Undo last edit(s)
2121
</button>
22+
<button class="btn btn-default btn-sm" (click)="angularGrid.filterService.clearFilters()">Clear Filters</button>
23+
<button class="btn btn-default btn-sm" (click)="angularGrid.sortService.clearSorting()">Clear Sorting</button>
2224
<label class="checkbox-inline control-label" for="autoCommitEdit">
2325
<input type="checkbox" id="autoCommitEdit" [value]="gridOptions.autoCommitEdit" (click)="changeAutoCommit()">
2426
Auto Commit Edit
25-
</label>
27+
</label>
2628
</span>
2729
</div>
2830
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
h3 {
2+
font-style: normal;
3+
color: #3d3d3d;
4+
}
5+
.subtitle {
6+
font-size: 18px;
7+
}
8+
.ng-select.custom {
9+
border:0px;
10+
max-height: 27px;
11+
border-radius: 0;
12+
}
13+
.ng-input {
14+
padding-top: -4px !important;
15+
padding-left: 4px !important;
16+
}

0 commit comments

Comments
 (0)