src/modules/table/components/column.component.ts
Component to display a column inside the StarkTableComponent
OnInit
OnChanges
changeDetection | ChangeDetectionStrategy.OnPush |
encapsulation | ViewEncapsulation.None |
host | { |
selector | stark-table-column |
templateUrl | ./column.component.html |
Properties |
|
Methods |
|
Inputs |
Outputs |
Accessors |
Public
constructor(renderer: Renderer2, elementRef: ElementRef)
|
||||||||||||
Class constructor
Parameters :
|
cellClassName | |
Type : | string
|
|
A function to generate classNames for cells based on the value, its row and the name of the column. Or a static string with the classNames. |
cellFormatter | |
Type : function
|
|
Function that returns a formatted value (string) to be set in the cell. It can be used to set different formats depending on the row, value and or columnName. This function is called with 3 parameters: |
compareFn | |
Type : function
|
|
Function that returns 1 : if obj1 > obj2 0 : if obj1 === obj2 -1 : if obj1 < obj2 |
dataAccessor | |
Type : function
|
|
Function that returns the raw value of this column in case the access to such value can't be provided via the column name. |
filterable | |
Type : boolean
|
|
Whether this column is filterable |
filterPosition | |
Type : MenuPositionY
|
|
Position where the column filter box should be displayed. Default: |
filterValue | |
Type : string
|
|
Value of the filter Wildcards can be used: "" to match any anything and "?" to match one character. Use "*" and "?" to match exactly the characters "" and "?" |
footerValue | |
Type : string | number | undefined
|
|
Value to be shown as the column's footer. |
headerClassName | |
Type : string
|
|
A static className for the header |
headerLabel | |
Type : string
|
|
Label to be shown as the column's header. Default: the column's name |
name | |
Type : string
|
|
Name of the property that will be the source of the column. |
sortable | |
Type : boolean
|
|
Default value : true
|
|
Whether the column is sortable or not. Default: |
sortDirection | |
Type : StarkTableColumnSortingDirection
|
|
Default value : ""
|
|
Sorting direction of the column. |
sortPriority | |
Type : number
|
|
Default value : 100
|
|
Priority of the column. |
stickyEnd | |
Type : boolean
|
|
Make the column stick to the right side of the table |
visible | |
Type : boolean
|
|
Whether the column is visible or not. Default: |
color | |
Type : string
|
|
Inherited from
AbstractStarkUiComponent
|
|
Defined in
AbstractStarkUiComponent:16
|
|
Color theme |
cellClicked | |
Type : EventEmitter
|
|
Output that will emit a StarkColumnCellClickedOutput whenever a cell in the column is clicked |
filterChanged | |
Type : EventEmitter
|
|
Output that will emit a specific column whenever its filter value has changed |
sortChanged | |
Type : EventEmitter
|
|
Output that will emit a specific column whenever its sorting direction has changed |
Public getCellClassNames | ||||||||
getCellClassNames(row: object)
|
||||||||
Gets a the classes for a specific cell, based on the cellClassName Input and the cellClassNameFn function if it was given.
Parameters :
Returns :
string
The classes for the cell. |
Public getDisplayedValue | ||||||||
getDisplayedValue(row: object)
|
||||||||
Get the final displayed value of the column formatter (if there was any defined in the column) and translating the value (in case the value is a translation key)
Parameters :
Returns :
string | number
The displayed value of the property from the given row item after applying the formatter (if there was any defined in the column) and translating the value (in case the value is a translation key) |
Public getHeaderClassNames |
getHeaderClassNames()
|
Get the classes for a header
Returns :
string
The classes for the header |
Public getRawValue | ||||||||
getRawValue(row: object)
|
||||||||
Get the raw value of the column
Parameters :
Returns :
any | undefined
The raw value of the property from the given row item |
Public ngOnChanges | ||||||||
ngOnChanges(simpleChanges: SimpleChanges)
|
||||||||
Component lifecycle hook
Parameters :
Returns :
void
|
Public ngOnInit |
ngOnInit()
|
Inherited from
AbstractStarkUiComponent
|
Defined in
AbstractStarkUiComponent:319
|
Component lifecycle hook
Returns :
void
|
Public onCellClick | ||||||||||||
onCellClick($event: Event, row: object)
|
||||||||||||
Called whenever a cell of the column is clicked
Parameters :
Returns :
void
|
Public onClearFilter |
onClearFilter()
|
Called when the Clear button in the filter pop-up is clicked
Returns :
void
|
Public onFilterChange |
onFilterChange()
|
Called whenever the value of the filter input changes
Returns :
void
|
Public onSortChange |
onSortChange()
|
Called whenever the sorting of the column changes
Returns :
void
|
Public columnDef |
Type : MatColumnDef
|
Decorators :
@ViewChild(MatColumnDef, {static: true})
|
Reference to the MatColumnDef embedded in this component |
Static ngAcceptInputType_filterable |
Type : BooleanInput
|
Static ngAcceptInputType_filterPosition |
Type : MenuPositionY | undefined
|
Static ngAcceptInputType_headerLabel |
Type : string | undefined
|
Static ngAcceptInputType_sortPriority |
Type : number | undefined
|
Static ngAcceptInputType_stickyEnd |
Type : BooleanInput
|
Static ngAcceptInputType_visible |
Type : BooleanInput
|
name | ||||||
getname()
|
||||||
Name of the property that will be the source of the column.
Returns :
string
|
||||||
setname(name: string)
|
||||||
Parameters :
Returns :
void
|
filterable | ||||||
getfilterable()
|
||||||
Whether this column is filterable
Returns :
boolean
|
||||||
setfilterable(value: boolean)
|
||||||
Parameters :
Returns :
void
|
headerLabel | ||||||
getheaderLabel()
|
||||||
Returns the header label of the column if it's specified. If not, simply returns the name of the column
Returns :
string
|
||||||
setheaderLabel(value: string)
|
||||||
Label to be shown as the column's header. Default: the column's name
Parameters :
Returns :
void
|
footerValue | ||||||
getfooterValue()
|
||||||
Returns the footer value of the column if it's specified.
Returns :
string | number | undefined
|
||||||
setfooterValue(value: string | number | undefined)
|
||||||
Value to be shown as the column's footer.
Parameters :
Returns :
void
|
filterPosition | ||||||
getfilterPosition()
|
||||||
Position where the column filter box should be displayed. Default:
Returns :
MenuPositionY
|
||||||
setfilterPosition(value: MenuPositionY)
|
||||||
Parameters :
Returns :
void
|
visible | ||||||
getvisible()
|
||||||
Whether the column is visible or not. Default:
Returns :
boolean
|
||||||
setvisible(value: boolean)
|
||||||
Parameters :
Returns :
void
|
stickyEnd | ||||||
getstickyEnd()
|
||||||
Make the column stick to the right side of the table
Returns :
boolean
|
||||||
setstickyEnd(value: boolean)
|
||||||
Parameters :
Returns :
void
|
import {
ChangeDetectionStrategy,
Component,
ContentChild,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
Renderer2,
SimpleChanges,
TemplateRef,
ViewChild,
ViewEncapsulation
} from "@angular/core";
import { AbstractStarkUiComponent } from "@nationalbankbelgium/stark-ui/src/internal-common";
import { BooleanInput, coerceBooleanProperty } from "@angular/cdk/coercion";
import { UntypedFormControl } from "@angular/forms";
import { LegacyMenuPositionY as MenuPositionY } from "@angular/material/legacy-menu";
import { MatLegacyColumnDef as MatColumnDef } from "@angular/material/legacy-table";
import { distinctUntilChanged } from "rxjs/operators";
import isEqual from "lodash-es/isEqual";
import get from "lodash-es/get";
import {
StarkColumnCellClickedOutput,
StarkColumnFilterChangedOutput,
StarkColumnSortChangedOutput,
StarkTableColumnSortingDirection
} from "../entities";
/**
* Component to display a column inside the StarkTableComponent
*/
@Component({
selector: "stark-table-column",
templateUrl: "./column.component.html",
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
// We need to use host instead of @HostBinding: https://github.com/NationalBankBelgium/stark/issues/664
host: {
class: "stark-table-column"
}
})
export class StarkTableColumnComponent extends AbstractStarkUiComponent implements OnInit, OnChanges {
/**
* Name of the property that will be the source of the column.
*/
@Input()
public get name(): string {
return this._name;
}
public set name(name: string) {
this._name = name;
this.columnDef.name = name;
}
/**
* @ignore
* @internal
*/
private _name = "";
/**
* Function that returns
* 1 : if obj1 > obj2
* 0 : if obj1 === obj2
* -1 : if obj1 < obj2
*
* @param obj1 - First object in the comparison
* @param obj2 - Second object in the comparison
*/
@Input()
public compareFn?: (obj1: object, obj2: object) => number;
/**
* Function that returns the raw value of this column in case the access to such value can't be provided via the column name.
*/
@Input()
public dataAccessor?: (data: object, name: string) => string; // TODO: really needed?
/**
* Function that returns a formatted value (string) to be set in the cell. It can be used to set different formats
* depending on the row, value and or columnName. This function is called with 3 parameters:
* @param value - The value of the cell to be formatted
* @param row - The row object that contains the cell
* @param columnName - The column that the cell belongs to
*/
@Input()
public cellFormatter?: (value: any, row?: object, columnName?: string) => string;
/**
* Sorting direction of the column.
*/
@Input()
public sortDirection: StarkTableColumnSortingDirection = "";
/**
* Whether this column is filterable
*/
@Input()
public get filterable(): boolean {
return this._filterable;
}
public set filterable(value: boolean) {
this._filterable = coerceBooleanProperty(value);
}
// Information about boolean coercion https://angular.io/guide/template-typecheck#input-setter-coercion
public static ngAcceptInputType_filterable: BooleanInput;
/**
* @ignore
* @internal
*/
private _filterable = false;
/**
* Value of the filter
* Wildcards can be used: "*" to match any anything and "?" to match one character.
* Use "\*" and "\?" to match exactly the characters "*" and "?"
*/
@Input()
public filterValue?: string;
/**
* Label to be shown as the column's header.
*
* Default: the column's name
*/
@Input()
public set headerLabel(value: string) {
this._headerLabel = value;
}
/**
* Returns the header label of the column if it's specified. If not, simply returns the name of the column
*/
public get headerLabel(): string {
return this._headerLabel || this.name;
}
public static ngAcceptInputType_headerLabel: string | undefined;
/**
* @ignore
* @internal
*/
private _headerLabel?: string;
/**
* Value to be shown as the column's footer.
*/
@Input()
public set footerValue(value: string | number | undefined) {
this._footerValue = value;
}
/**
* Returns the footer value of the column if it's specified.
*/
public get footerValue(): string | number | undefined {
return this._footerValue;
}
/**
* @ignore
* @internal
*/
private _footerValue?: string | number;
/**
* Whether the column is sortable or not.
*
* Default: `true`
*/
@Input()
public sortable = true;
/**
* Position where the column filter box should be displayed.
*
* Default: `"below"`
*/
@Input()
public get filterPosition(): MenuPositionY {
return this._filterPosition;
}
public set filterPosition(value: MenuPositionY) {
this._filterPosition = value || "below";
}
public static ngAcceptInputType_filterPosition: MenuPositionY | undefined;
/**
* @ignore
* @internal
*/
private _filterPosition: MenuPositionY = "below";
/**
* Priority of the column.
*/
@Input()
public sortPriority = 100;
public static ngAcceptInputType_sortPriority: number | undefined;
/**
* Whether the column is visible or not.
*
* Default: `true`
*/
@Input()
public get visible(): boolean {
return this._visible;
}
public set visible(value: boolean) {
this._visible = coerceBooleanProperty(value);
}
// Information about boolean coercion https://angular.io/guide/template-typecheck#input-setter-coercion
public static ngAcceptInputType_visible: BooleanInput;
/**
* @ignore
* @internal
*/
private _visible = true;
/**
* A function to generate classNames for cells based on the value, its row and the name of the column.
* Or a static string with the classNames.
*/
@Input()
public cellClassName?: ((value: any, row?: object, columnName?: string) => string) | string;
/**
* A static className for the header
*/
@Input()
public headerClassName?: string;
/**
* Make the column stick to the right side of the table
*/
@Input()
public get stickyEnd(): boolean {
return this._stickyEnd;
}
public set stickyEnd(value: boolean) {
this._stickyEnd = coerceBooleanProperty(value);
}
// Information about boolean coercion https://angular.io/guide/template-typecheck#input-setter-coercion
public static ngAcceptInputType_stickyEnd: BooleanInput;
/**
* @ignore
* @internal
*/
private _stickyEnd = false;
/**
* Output that will emit a StarkColumnCellClickedOutput whenever a cell in the column is clicked
*/
@Output()
public readonly cellClicked = new EventEmitter<StarkColumnCellClickedOutput>();
/**
* Output that will emit a specific column whenever its filter value has changed
*/
@Output()
public readonly filterChanged = new EventEmitter<StarkColumnFilterChangedOutput>();
/**
* Output that will emit a specific column whenever its sorting direction has changed
*/
@Output()
public readonly sortChanged = new EventEmitter<StarkColumnSortChangedOutput>();
/**
* Reference to the MatColumnDef embedded in this component
*/
@ViewChild(MatColumnDef, { static: true })
public columnDef!: MatColumnDef;
/**
* Reference to the transcluded template in this component via the ngTemplateOutlet
*/
@ContentChild(TemplateRef, { static: true })
// eslint-disable-next-line no-null/no-null
public columnTemplate: TemplateRef<object> | null = null;
/**
* @ignore
* Internal formControl to manage the filter value of the column
*/
public _filterFormCtrl = new UntypedFormControl();
/**
* Class constructor
* @param renderer - Angular `Renderer2` wrapper for DOM manipulations.
* @param elementRef - Reference to the DOM element where this component is attached to.
*/
// eslint-disable-next-line no-useless-constructor
public constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef);
}
/**
* Component lifecycle hook
*/
public override ngOnInit(): void {
super.ngOnInit();
this._filterFormCtrl.valueChanges.pipe(distinctUntilChanged()).subscribe((value?: string | null) => {
// eslint-disable-next-line no-null/no-null
this.filterValue = value === null ? undefined : value;
this.filterChanged.emit({
filterValue: this.filterValue,
name: this.name
});
});
}
/**
* Component lifecycle hook
* @param simpleChanges - Contains the changed properties
*/
public ngOnChanges(simpleChanges: SimpleChanges): void {
if (
simpleChanges["filterValue"] &&
!isEqual(simpleChanges["filterValue"].previousValue, simpleChanges["filterValue"].currentValue)
) {
this._filterFormCtrl.setValue(this.filterValue);
}
if (simpleChanges["sortPriority"] && typeof simpleChanges["sortPriority"].currentValue === "undefined") {
this.sortPriority = 100;
}
}
/**
* Get the raw value of the column
* @param row - The row item
* @returns The raw value of the property from the given row item
*/
public getRawValue(row: object): any | undefined {
let rawValue: any | undefined = get(row, this.name);
if (this.dataAccessor) {
rawValue = this.dataAccessor(row, this.name);
}
// ensure we always return undefined instead of null
// eslint-disable-next-line no-null/no-null
return rawValue !== null ? rawValue : undefined;
}
/**
* Called whenever a cell of the column is clicked
* @param $event - The handled event
* @param row - The row item
*/
public onCellClick($event: Event, row: object): void {
if (this.cellClicked.observers.length > 0) {
$event.stopPropagation();
this.cellClicked.emit({
value: this.getRawValue(row),
row: row,
columnName: this.name
});
}
}
/**
* Get the final displayed value of the column
* @param row - The row item
* @returns The displayed value of the property from the given row item after applying the
* formatter (if there was any defined in the column) and translating the value (in case the value is a translation key)
*/
public getDisplayedValue(row: object): string | number {
let formattedValue = "";
const rawValue: any | undefined = this.getRawValue(row);
if (this.cellFormatter instanceof Function) {
formattedValue = this.cellFormatter(rawValue, row, this.name);
} else if (typeof rawValue === "undefined") {
return ""; // return already, no point in translating an empty string
} else if (typeof rawValue === "number") {
return rawValue; // return already, no point in translating a number
} else {
formattedValue = rawValue.toString();
}
// TODO: add translation feature
// return this.$translate.instant(formattedValue);
return formattedValue;
}
/**
* Called when the Clear button in the filter pop-up is clicked
*/
public onClearFilter(): void {
this._filterFormCtrl.reset();
this.onFilterChange();
}
/**
* Called whenever the value of the filter input changes
*/
public onFilterChange(): void {
this.filterChanged.emit(this);
}
/**
* Called whenever the sorting of the column changes
*/
public onSortChange(): void {
this.sortChanged.emit({
name: this.name,
sortable: this.sortable,
sortDirection: this.sortDirection,
sortPriority: this.sortPriority
});
}
/**
* Gets a the classes for a specific cell, based on the cellClassName Input and the cellClassNameFn function if it was given.
* @param row - The data object of the row the cell is in.
* @returns The classes for the cell.
*/
public getCellClassNames(row: object): string {
if (!this.visible) {
return "hidden";
}
if (!this.cellClassName) {
return "";
}
if (typeof this.cellClassName === "string") {
return this.cellClassName;
}
const value: any = this.getRawValue(row);
return this.cellClassName(value, row, this.name);
}
/**
* Get the classes for a header
* @returns The classes for the header
*/
public getHeaderClassNames(): string {
const classes: string[] = [];
if (!this.visible) {
classes.push("hidden");
}
if (this.sortable) {
classes.push("sortable");
}
if (this.filterValue) {
classes.push("filtering");
}
if (this.headerClassName) {
classes.push(this.headerClassName);
}
return classes.join(" ");
}
}
<ng-container matColumnDef [stickyEnd]="stickyEnd">
<!-- custom header that supports multi-column sorting -->
<!-- TODO: implement a MultiSort directive based on the Angular Material's MatSort once this is solved: https://github.com/angular/material2/issues/7226 -->
<th mat-header-cell *matHeaderCellDef [ngClass]="getHeaderClassNames()">
<div class="header-cell-content">
<div [ngSwitch]="sortDirection" class="sort-header" (click)="onSortChange()">
<span>{{ headerLabel | translate }}</span>
<ng-container *ngIf="sortable" [ngSwitch]="sortDirection">
<mat-icon *ngSwitchCase="'asc'" svgIcon="arrow-up" class="stark-small-icon"></mat-icon>
<mat-icon *ngSwitchCase="'desc'" svgIcon="arrow-down" class="stark-small-icon"></mat-icon>
<mat-icon class="order-tip stark-small-icon" *ngSwitchDefault svgIcon="arrow-up"></mat-icon>
</ng-container>
<span *ngIf="sortPriority < 100 && sortDirection" class="priority">{{ sortPriority }}</span>
</div>
<button *ngIf="filterable" class="button-filter" [matMenuTriggerFor]="filterMenu" mat-icon-button>
<mat-icon class="stark-small-icon" svgIcon="filter" [matTooltip]="'STARK.TABLE.COLUMN_FILTER' | translate"></mat-icon>
</button>
</div>
<mat-menu class="mat-table-filter" #filterMenu="matMenu" [yPosition]="filterPosition" xPosition="before" [overlapTrigger]="false">
<div>
<mat-form-field
class=""
(click)="$event.stopPropagation()"
(keyup)="$event.stopPropagation()"
(keydown)="$event.stopPropagation()"
>
<input matInput [placeholder]="'STARK.TABLE.FILTER' | translate" name="filter" [formControl]="_filterFormCtrl" />
</mat-form-field>
<button mat-icon-button (click)="onClearFilter()">
<mat-icon svgIcon="close" [matTooltip]="'STARK.TABLE.CLEAR_FILTER' | translate"></mat-icon>
</button>
</div>
</mat-menu>
</th>
<!-- the column template defined by the user will be displayed here -->
<!-- and it will receive the right context containing the displayedValue and the row data-->
<td mat-cell *matCellDef="let rowItem" [ngClass]="getCellClassNames(rowItem)" (click)="onCellClick($event, rowItem)">
<ng-container
*ngTemplateOutlet="
columnTemplate;
context: { $implicit: { rowData: rowItem, rawValue: getRawValue(rowItem), displayedValue: getDisplayedValue(rowItem) } }
"
></ng-container>
</td>
<ng-container *ngIf="footerValue !== 'undefined'">
<td mat-footer-cell *matFooterCellDef>
{{ footerValue!.toString() | translate }}
</td>
</ng-container>
</ng-container>