Skip to content

Instantly share code, notes, and snippets.

@UmerIftikhar
Last active December 21, 2023 07:42
Show Gist options
  • Save UmerIftikhar/17b52dfe598f2e05e94d7bacca434f22 to your computer and use it in GitHub Desktop.
Save UmerIftikhar/17b52dfe598f2e05e94d7bacca434f22 to your computer and use it in GitHub Desktop.
export class Constants {
public static apiRoot = '/api', // If there is some prefix for all the APIs, then define that in constants.
public static API_VERSIONS = {
Version1: 'version1',
};
public static pageSettings(): PageEvent {
return {
length: 0,
pageIndex: 0,
pageSize: 10,
};
}
public static pageSizeOptions: number[] = [10, 25, 50, 100];
}
<p>
<mat-form-field>
<input matInput (keyup)="applySearchFilter($event.target.value)" placeholder="Search Data">
</mat-form-field>
<button *ngIf="displayCreateAction" mat-fab (click)="performAction(userActions.Add, {})"
matTooltipClass="tooltip" matTooltipPosition="left" [matTooltip]="addRowText"
mat-flat-button color="primary" style="float:right">
<mat-icon>add</mat-icon>
</button>
</p>
<div class="table-container">
<table mat-table [dataSource]="dataSource" matSort>
<caption></caption>
<ng-container *ngFor="let columnId of objectKeys(columnDefinition)" [matColumnDef]="columnId">
<ng-container *ngIf="columnId==='CRUD_OPERATION'">
<th mat-header-cell *matHeaderCellDef class="table-header" id="CRUD_OPERATION">
<button mat-fab matTooltipClass="tooltip"
(click)="performAction(userActions.Add, {})" [matTooltip]="addRowText"
matTooltipPosition="left" mat-flat-button color="primary" style="float:right">
<mat-icon class="mat-18">add</mat-icon>
</button>
</th>
<td mat-cell *matCellDef="let element">
<div class="actions-buttons">
<button *ngIf="displayDetailAction" mat-icon-button color="primary" matTooltipClass="tooltip" [matTooltip]="viewDetailRowText"
(click)="performAction(userActions.Details, element)">
<mat-icon class="mat-18">reorder</mat-icon>
</button>
<button *ngIf="displayEditAction" mat-icon-button color="primary" matTooltipClass="tooltip" [matTooltip]="editRowText"
(click)="performAction(userActions.Update, element)">
<mat-icon class="mat-18">edit</mat-icon>
</button>
<button *ngIf="displayDeleteAction" mat-icon-button color="primary" matTooltipClass="tooltip" [matTooltip]="deleteRowText"
(click)="performAction(userActions.Delete, element)">
<mat-icon class="mat-18">delete</mat-icon>
</button>
</div>
</td>
</ng-container>
<th id="{{columnId}}" mat-header-cell *matHeaderCellDef mat-sort-header class="table-header"> {{columnDefinition[columnId]}} </th>
<td mat-cell *matCellDef="let element"> {{element[columnId] }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="objectKeys(columnDefinition); sticky: true" class="table-header"></tr>
<tr class="row-custom-class" mat-row *matRowDef="let row; columns: objectKeys(columnDefinition); let i=index" [attr.id]="i"></tr>
</table>
<mat-paginator (page)="pageChanged($event)" [pageSize]="pageProperties.pageSize"
[length]="pageProperties.length"
[pageSizeOptions]="pageSizeOptions" class="mat-paginator-sticky" showFirstLastButtons="true"></mat-paginator>
</div>
import { Component, Input, Output, EventEmitter, ViewChild, OnChanges } from '@angular/core';
import { MatTable } from '@angular/material';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Constants } from './constants';
import { UserActions } from './helpers/';
@Component({
selector: 'app-generic-table',
templateUrl: './generic-table.component.html',
styleUrls: ['./generic-table.component.scss'],
})
export class BaseTableComponent implements OnChanges {
// Its a key value pair, where key should be matching the columns in the table data, where as
// the value should be the column display name.
@Input() columnDefinition: any = {};
// This is an array of the table data which needs to be displayed.
@Input() tableData: any[];
// pixles after which the table data must be over flowed.
@Input() tableOverFlowLimit: any;
dataSource: MatTableDataSource<any>; // its any since, the data type is defined at runtime by the parent component.
pageSize = Constants.pageSettings().pageSize; // default page size for the Table.
pageSizeOptions: number[] = Constants.pageSizeOptions; // This tells how many items can be displayed per page.
pageProperties: PageEvent = Constants.pageSettings();
// Column definition for the CRUD operations.
actionColumnName = 'Actions';
// since, its a crud table, therefore an enum has been defined representing those actions.
readonly userActions: typeof UserActions = UserActions;
readonly objectKeys = Object.keys;
// If this is set to true, then each time the user click the next page, an API call will be made to fetch the data.
@Input() enableBackendPagination = false;
// If this is set to true, then each time the user types something in the search text, an API call will be made to fetch the data.
@Input() enableBackendSearch = false;
// Tool Tips for the respective CRUD Operations. [define the absolute values from the parent]
@Input() readonly addRowText: string;
@Input() readonly editRowText: string;
@Input() readonly deleteRowText: string;
@Input() readonly viewDetailRowText: string;
// Actions to be displayed for respective Table. [dnt forget to actionCalled event if any of them is enabled]
@Input() readonly displayEditAction: boolean = true;
@Input() readonly displayDeleteAction: boolean = true;
@Input() readonly displayDetailAction: boolean = true;
@Input() readonly displayCreateAction: boolean = true;
// This is called to notify the parent which CRUD operation is called, along with the current row object.
@Output() readonly actionCalled = new EventEmitter();
// This is called to notify the parent when the next, previous or items per page is clicked.
// If enableBackendPagination is set to true, a call is made to the Parent component, which in turn calls the API.
@Output() readonly pageEventCalled = new EventEmitter();
// This is called to notify the parent when the user types in something in the search field.
// If enableBackendSearch is set to true, a call is made to the Parent component, which in turn calls the API.
@Output() readonly searchCalled = new EventEmitter();
// Respective components as per Angular Material Design Guidelines.
@ViewChild(MatTable, { static: true }) table: MatTable<any>;
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
constructor() {
this.dataSource = new MatTableDataSource([]);
}
ngOnChanges() {
this.columnDefinition.CRUD_OPERATION = this.actionColumnName;
this.dataSource.data = this.tableData || [];
this.dataSource.sort = this.sort;
if (!this.enableBackendPagination) {
this.dataSource.paginator = this.paginator;
}
}
applySearchFilter(filterValue: string) {
const valueToSearch = filterValue.trim().toLowerCase();
if(enableBackendSearch) {
this.searchCalled.emit(valueToSearch);
}
if (this.enableBackendPagination) {
this.paginator.firstPage();
} else {
this.dataSource.filter = valueToSearch;
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
}
performAction(action, obj) {
// If actionCalled is not defined, then simply return.
// Although if actions are enabled then actionCalled should also be defined as well.
if (!this.actionCalled) {
return;
}
this.actionCalled.emit({
action,
obj,
});
}
pageChanged(pageEvent: PageEvent) {
if (!pageEvent || !this.pageEventCalled) {
return;
}
this.pageEventCalled.emit(pageEvent);
}
}
export enum UserActions {
Add = 'ADD',
Details = 'DETAILS',
Update = 'UPDATE',
Delete = 'DELETE',
Cancel = 'CANCEL',
}
@daugustowski
Copy link

daugustowski commented May 17, 2023

[generic-table.component.html]
Line 46:
<td mat-cell *matCellDef="let element"> {{element[columnId] }}</td>
Should be:
<td mat-cell *matCellDef="let element"> {{element[columnDefinition[columnId]] }}</td>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment