nm-tree-grid
src/app/shared/widgets/tree-grid/tree-grid.component.ts
providers |
DataListService
|
selector | nm-tree-grid |
styleUrls | tree-grid.component.scss |
templateUrl | ./tree-grid.component.html |
Widget inputs |
Widget outputs |
Properties |
|
Methods |
|
constructor(authHttp: HttpClient, scrollService: ScrollService)
|
|||||||||
Parameters :
|
Public clearColumnFilter |
clearColumnFilter(input: any, column: any)
|
Returns :
void
|
Protected configureWidget | ||||||
configureWidget(configuration: WidgetConfig)
|
||||||
Decorators : WidgetConfigure
|
||||||
Parameters :
Returns :
void
|
Protected doReset |
doReset()
|
Returns :
void
|
Public formatIncomingData | ||||
formatIncomingData(treeData: )
|
||||
Parameters :
Returns :
{}
|
handleRowSelection | ||||
handleRowSelection(event: )
|
||||
Parameters :
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
Public onColumnFilterInput | |||||||||
onColumnFilterInput(input: any, column: IgxColumnComponent)
|
|||||||||
Parameters :
Returns :
void
|
onRowSelection | ||||
onRowSelection(event: )
|
||||
Parameters :
Returns :
void
|
Public stopPropagation | ||||||
stopPropagation(event: , field: )
|
||||||
Parameters :
Returns :
boolean
|
Public _id |
_id:
|
Decorators : WidgetId
|
Public allowFiltering |
allowFiltering:
|
Type : boolean
|
Public authHttp |
authHttp:
|
Type : HttpClient
|
Private clearFiltersAndSortingChannel |
clearFiltersAndSortingChannel:
|
Type : ReplaySubject<any>
|
Default value : new ReplaySubject<any>()
|
Decorators : WidgetInput
|
Public columnFilterInputs |
columnFilterInputs:
|
Type : any
|
Default value : {}
|
Public columns |
columns:
|
Type : Column[]
|
Default value : []
|
Public configuration |
configuration:
|
Type : WidgetConfig<DataListConfiguration>
|
Decorators : WidgetConfiguration
|
Public data |
data:
|
Type : any[]
|
Default value : []
|
Public dataType |
dataType:
|
Type : string
|
Public disableSelection |
disableSelection:
|
Type : boolean
|
Public fallback |
fallback:
|
Type : string
|
Default value : contextPath + "/assets/placeholder_281x202.jpg"
|
Public filterMode |
filterMode:
|
Type : string
|
Public foreignKey |
foreignKey:
|
Type : string
|
Public header |
header:
|
Public hideOnEmpty |
hideOnEmpty:
|
Type : boolean
|
Default value : false
|
Public hideTable |
hideTable:
|
Type : Subject<boolean>
|
Default value : new ReplaySubject<boolean>()
|
Decorators : WidgetInput
|
The hide that should be used to hide tree grid |
Public infoText |
infoText:
|
Type : string
|
Default value : "list.results"
|
Public inputBoxes |
inputBoxes:
|
Type : QueryList<ElementRef>
|
Decorators : ViewChildren
|
Private inputChannel |
inputChannel:
|
Type : ReplaySubject<any>
|
Default value : new ReplaySubject<any>()
|
Decorators : WidgetInput
|
The uri that should be used to fetch the data |
Private inputData |
inputData:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetInput
|
Display already fetched data |
Public isCollapsible |
isCollapsible:
|
Public multiSelection |
multiSelection:
|
Type : boolean
|
Default value : true
|
Public primaryKey |
primaryKey:
|
Type : string
|
Private resetChannel |
resetChannel:
|
Type : ReplaySubject<any>
|
Default value : new ReplaySubject<any>()
|
Decorators : WidgetInput
|
Public results |
results:
|
Type : Subject<any[]>
|
Default value : new BehaviorSubject([])
|
Decorators : WidgetOutput
|
Public selectedItem |
selectedItem:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetOutput
|
Public selectedItems |
selectedItems:
|
Type : Subject<any>
|
Default value : new Subject<any>()
|
Decorators : WidgetOutput
|
Emits all selected items whenever they change |
Public selectedUiLocale |
selectedUiLocale:
|
Default value : new ReplaySubject<any>(1)
|
Decorators : WidgetInput
|
Public selection |
selection:
|
Type : boolean
|
Default value : false
|
Public tableHeight |
tableHeight:
|
Type : string
|
Public title |
title:
|
Public total |
total:
|
Type : number
|
Public treeGrid |
treeGrid:
|
Type : IgxTreeGridComponent
|
Decorators : ViewChild
|
Public treeHeight |
treeHeight:
|
Type : string
|
Private unsubscribe |
unsubscribe:
|
Default value : NgUnsubscribe.create()
|
Public wikiLink |
wikiLink:
|
Type : string
|
Public withHeader |
withHeader:
|
Type : boolean
|
Default value : true
|
import {
Component,
ElementRef,
OnInit,
QueryList,
ViewChild,
ViewChildren,
HostListener,
OnDestroy,
} from "@angular/core";
import {
WidgetComponent,
WidgetId,
WidgetConfiguration,
WidgetConfigure,
WidgetInput,
WidgetOutput,
} from "../widget.metadata";
import {
WidgetConfig,
getOrDefault,
throwIfUndefined,
} from "../widget.configuration";
import {
empty as observableEmpty,
of as observableOf,
timer as observableTimer,
Observable,
Subject,
BehaviorSubject,
ReplaySubject,
} from "rxjs";
import {
distinctUntilChanged,
withLatestFrom,
map,
takeUntil,
debounceTime,
switchMap,
mergeMap,
catchError,
} from "rxjs/operators";
import { NgUnsubscribe } from "../../ng-unsubscribe";
import { DataListService } from "../data-list/data-list.service";
import { HttpClient } from "@angular/common/http";
import { Link, MultiSelectAction, Resource } from "../../components/hal/hal";
import {
IgxColumnComponent,
IgxStringFilteringOperand,
IgxTreeGridComponent,
} from "@infragistics/igniteui-angular";
import { ScrollService } from "../../components/scroll/scroll.service";
declare var contextPath: string;
interface Column {
field: string;
header: string;
width: number;
filter?: boolean;
sort?: boolean;
sortable?: boolean;
type?: string;
linkTemplate?: string;
linkType?: string;
pinned?: boolean;
"filter-type"?: "contains" | undefined;
}
interface DataListConfiguration {
title: string;
columns: Column[];
defaultPageSize: number;
debounceTime: number;
dataType: string;
header: string;
infoText: string;
wikiLink: string;
isCollapsible: boolean;
attributeUrl: string;
localStorageShownAttributes: string;
withHeader: boolean;
allowFiltering: boolean;
}
export interface Result extends Resource {
description?: string;
identifier?: string;
}
export interface ResultResource extends Resource {
total?: number;
_embedded?: {
products: Result[];
};
_actions?: {
multiSelect: MultiSelectAction;
};
_links?: {
custom: Link;
};
}
@WidgetComponent("nm-tree-grid")
@Component({
providers: [DataListService],
selector: "nm-tree-grid",
styleUrls: ["./tree-grid.component.scss"],
templateUrl: "./tree-grid.component.html",
})
export class TreeGridComponent implements OnDestroy {
public data: any[] = [];
public dataType: string;
public total: number;
public columns: Column[] = [];
public multiSelection: boolean = true;
public disableSelection: boolean;
public title;
public header;
public isCollapsible;
public filterMode: string;
public columnFilterInputs: any = {};
public infoText: string = "list.results";
public wikiLink: string;
public withHeader: boolean = true;
public tableHeight: string;
public allowFiltering: boolean;
public treeHeight: string;
public fallback: string = contextPath + "/assets/placeholder_281x202.jpg";
public primaryKey: string;
public foreignKey: string;
/**
*Emits all selected items whenever they change
*/
@WidgetOutput("selectedItems")
public selectedItems: Subject<any> = new Subject<any>();
/**
* The hide that should be used to hide tree grid
*/
@WidgetInput("hide")
public hideTable: Subject<boolean> = new ReplaySubject<boolean>();
/**
* The uri that should be used to fetch the data
*/
@WidgetInput("uri")
private inputChannel: ReplaySubject<any> = new ReplaySubject<any>();
/**
* Display already fetched data
*/
@WidgetInput("inputData")
private inputData: Subject<any> = new Subject<any>();
//Emits currently visible data
@WidgetOutput("results")
public results: Subject<any[]> = new BehaviorSubject([]);
@WidgetId()
public _id;
@WidgetConfiguration()
public configuration: WidgetConfig<DataListConfiguration>;
@WidgetInput("reset")
private resetChannel: ReplaySubject<any> = new ReplaySubject<any>();
@WidgetInput("clearFiltersAndSorting")
private clearFiltersAndSortingChannel: ReplaySubject<any> = new ReplaySubject<any>();
@WidgetInput()
public selectedUiLocale = new ReplaySubject<any>(1);
@ViewChild("treeGrid")
public treeGrid: IgxTreeGridComponent;
@WidgetOutput("selectedItem")
public selectedItem: Subject<any> = new Subject<any>();
@ViewChildren("inputBox")
public inputBoxes: QueryList<ElementRef>;
public selection: boolean = false;
private unsubscribe = NgUnsubscribe.create();
public hideOnEmpty: boolean = false;
constructor(
public authHttp: HttpClient,
private scrollService: ScrollService
) {}
@WidgetConfigure()
protected configureWidget(configuration: WidgetConfig) {
this.header = getOrDefault(
this.configuration.configuration.header,
"primary"
);
this.infoText = configuration.configuration.infoText;
this.title = this.configuration.configuration.title;
this.isCollapsible = getOrDefault(
this.configuration.configuration.isCollapsible,
false
);
this.wikiLink = this.configuration.configuration.wikiLink;
this.withHeader = getOrDefault(
this.configuration.configuration.withHeader,
true
);
this.columns = this.configuration.configuration.columns["data"];
this.hideOnEmpty = configuration.configuration["hideOnEmpty"] || false;
this.dataType = configuration.configuration["dataType"];
if (!configuration.configuration.unlimitedHeight) {
this.tableHeight = getOrDefault(
String(configuration.configuration.tableHeight) + "px",
"100%"
);
}
//quickFilter || excelStyleFilter || inlineFilter
this.filterMode = getOrDefault(
this.configuration.configuration["filterMode"],
"inlineFilter"
);
this.disableSelection = getOrDefault(
configuration.configuration["disableSelection"],
false
);
this.allowFiltering = getOrDefault(
configuration.configuration["allowFilter"],
true
);
this.treeHeight = getOrDefault(
configuration.configuration["height"],
"451px"
);
this.primaryKey = getOrDefault(
configuration.configuration["primaryKey"],
"pimRef"
);
this.foreignKey = getOrDefault(
configuration.configuration["foreignKey"],
"parentPimRef"
);
if (this.disableSelection) {
this.multiSelection = false;
} else {
this.multiSelection = getOrDefault(
configuration.configuration["multiSelection"],
true
);
}
this.clearFiltersAndSortingChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => {
if (this.treeGrid) {
this.treeGrid.clearFilter();
this.treeGrid.clearSort();
this.columnFilterInputs = [];
this.treeGrid.cdr.detectChanges();
}
});
this.resetChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => {
this.data = [];
});
this.hideTable
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((hide) => {
this.hideOnEmpty = hide;
});
this.scrollService
.getChangeViewPortSize()
.pipe(takeUntil(this.unsubscribe))
.subscribe((status) => {
// Waiting for animation to finish
setTimeout(() => {
if (this.treeGrid) {
this.treeGrid.reflow();
}
}, 200);
});
this.inputData
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((data) => {
this.data = this.formatIncomingData(data);
this.results.next(this.data);
this.hideOnEmpty = false;
});
this.inputChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe((link) => {
if (link !== null && link !== undefined) {
this.authHttp
.get(link, {})
.pipe(
debounceTime(400),
distinctUntilChanged(),
map((res) => {
return <ResultResource>res;
})
)
.subscribe((data: any) => {
this.data = this.formatIncomingData(
data._embedded[this.dataType]
);
this.results.next(this.data);
this.hideOnEmpty = false;
});
}
});
this.resetChannel
.asObservable()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => {
this.doReset();
});
}
protected doReset() {
this.data = [];
this.results.next(this.data);
this.hideOnEmpty = true;
}
public formatIncomingData(treeData) {
let data = [];
if (treeData) {
for (let row of treeData) {
if (!row["id"]) {
row["id"] = row[this.primaryKey];
}
if (
row[this.foreignKey] &&
row[this.foreignKey] === row[this.primaryKey]
) {
row["parentId"] = -1;
} else if (!row["parentId"]) {
row["parentId"] = row[this.foreignKey];
}
data.push(row);
}
}
return data;
}
public stopPropagation(event, field) {
event.stopImmediatePropagation();
event.preventDefault();
this.inputBoxes.forEach((input) => {
if (input.nativeElement.classList.contains(field)) {
input.nativeElement.focus();
}
});
return false;
}
public onColumnFilterInput(input: any, column: IgxColumnComponent) {
let operand = IgxStringFilteringOperand.instance().condition("contains");
this.treeGrid.filter(
column.field,
input,
operand,
column.filteringIgnoreCase
);
}
public clearColumnFilter(input: any, column: any) {
this.columnFilterInputs[column.field] = null;
this.treeGrid.clearFilter(column.field);
}
handleRowSelection(event) {
let selectedRows = event;
if (this.multiSelection == false && !this.disableSelection) {
if (event.cell) {
this.treeGrid.deselectAllRows();
if (event.cell.row.isSelected) {
this.treeGrid.deselectRows([event.cell.row.rowID]);
selectedRows = null;
} else {
this.treeGrid.selectRows([event.cell.row.rowID]);
}
}
this.selectedItem.next(selectedRows);
}
}
onRowSelection(event) {
let result = {
count: event.newSelection ? event.newSelection.length : 1,
rows: event.newSelection,
rowData: [],
};
result.rowData = result.rows.map((key) => {
return this.treeGrid.getRowByKey(key)
? this.treeGrid.getRowByKey(key).rowData
: {};
});
}
ngOnDestroy() {
this.unsubscribe.destroy();
}
}
<nm-widgetframe
[header]="header"
[configuration]="configuration"
[infoTitle]="title"
[infoText]="infoText"
[infoPlacement]="'bottom'"
[wikiLink]="wikiLink"
[isCollapsible]="isCollapsible"
[toolbarInvisible]="!withHeader"
*ngIf="!hideOnEmpty"
>
<div slot="title" class="nm-widgetframe__title">
<span>{{ title | translate }} <span *ngIf="total">( {{total} )</span></span>
</div>
<div slot="content" class="nm-widgetframe__content" [ngClass]="filterMode">
<igx-tree-grid
#treeGrid
[data]="data"
[allowFiltering]="true"
primaryKey="id"
foreignKey="parentId"
[hideRowSelectors]="multiSelection"
(cellClick)="handleRowSelection($event)"
(rowSelected)="onRowSelection($event)"
[autoGenerate]="false"
[filterMode]="filterMode"
expansionDepth="1"
[height]="tableHeight"
>
<igx-column
*ngFor="let nmColumn of columns"
[field]="nmColumn.field"
[sortable]="nmColumn.sortable"
[filterable]="nmColumn.filter"
[header]="nmColumn.header | translate"
[pinned]="nmColumn.pinned"
[hidden]="nmColumn.hidden"
[movable]="true"
[resizable]="true"
[width]="nmColumn.width"
[dataType]="nmColumn.type"
>
<ng-template igxHeader let-column *ngIf="filterMode === 'inlineFilter'">
<div
class="title-inner"
[attr.draggable]="false"
[ngClass]="{ widthSorting: nmColumn.sortable }"
>
<span *ngIf="!nmColumn.filter" class="nm-title-container">{{
nmColumn.header | translate
}}</span>
<mat-form-field
*ngIf="nmColumn.filter"
class="nm-list-column-search"
[attr.draggable]="false"
>
<input
[attr.draggable]="false"
#inputBox
[ngClass]="column.field"
matInput
type="text"
(click)="stopPropagation($event, column.field)"
[placeholder]="nmColumn.header | translate"
[(ngModel)]="columnFilterInputs[column.field]"
(input)="
onColumnFilterInput(columnFilterInputs[column.field], column)
"
/>
<button
(click)="$event.stopPropagation()"
[attr.draggable]="false"
mat-button
*ngIf="columnFilterInputs[column.field]"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="
clearColumnFilter(columnFilterInputs[column.field], column)
"
>
<mat-icon [attr.draggable]="false">close</mat-icon>
</button>
</mat-form-field>
<mat-icon
class="
nm-table-head-icon nm-table-pin
fa fa fa-thumb-tack
fade-in
"
[class.pinned]="column.pinned"
[attr.draggable]="false"
(click)="column.pinned = !column.pinned"
></mat-icon>
</div>
</ng-template>
<ng-template igxCell let-val let-cell="cell">
<ng-container [ngSwitch]="nmColumn.type">
<div *ngSwitchCase="'hal-actions'">
<div class="hal-actions">
<span
*ngFor="let act of cell.row.rowData._actions | iterable"
id="{{ act.key }}"
>
<ng-container *ngIf="act.value.hidden !== true">
<nm-action-icon
*ngIf="act.key != 'download'"
[action]="act.value"
[name]="act.key"
>
</nm-action-icon>
</ng-container>
</span>
</div>
</div>
<div *ngSwitchDefault style="width: 100%">
<nm-ellipsis
[content]="cell.row.rowData[nmColumn.field]"
></nm-ellipsis>
</div>
</ng-container>
</ng-template>
</igx-column>
</igx-tree-grid>
</div>
</nm-widgetframe>