Skip to content

Commit

Permalink
Merge pull request #234 from e-picsa/feat/data-table-custom
Browse files Browse the repository at this point in the history
feat(core): data table custom value templates and header formatter
  • Loading branch information
chrismclarke authored Feb 15, 2024
2 parents 2ac6965 + 928e062 commit 3103130
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
17 changes: 15 additions & 2 deletions libs/shared/src/features/data-table/data-table.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,23 @@
<table mat-table matSort [dataSource]="dataSource" [matSortDisabled]="!tableOptions.sort">
@for(column of tableOptions.displayColumns; track column){
<ng-container [matColumnDef]="column">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ column }}</th>
<td mat-cell *matCellDef="let el">{{ el[column] }}</td>
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ column | formatValue: tableOptions.formatHeader }}</th>

<td mat-cell *matCellDef="let el">
@if(valueTemplates[column]){
<!-- custom value templates -->
<ng-container
[ngTemplateOutlet]="valueTemplates[column]"
[ngTemplateOutletContext]="{ $implicit: el[column] }"
></ng-container>
} @else {
<!-- Default value template -->
{{ el[column] }}
}
</td>
</ng-container>
}

<tr mat-header-row *matHeaderRowDef="tableOptions.displayColumns"></tr>
<tr
mat-row
Expand Down
61 changes: 58 additions & 3 deletions libs/shared/src/features/data-table/data-table.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, ViewChild } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Input,
OnChanges,
Pipe,
PipeTransform,
TemplateRef,
ViewChild,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { capitalise } from '@picsa/utils';
import download from 'downloadjs';
import { unparse } from 'papaparse';

Expand All @@ -21,15 +32,50 @@ export interface IDataTableOptions {
search?: boolean;
/** Specify whether to include column sort headers (default true) */
sort?: boolean;
/** Apply custom formatter to header values. Default replaces underscore with space and capitalises each word */
formatHeader?: (value: string) => string;
/** Bind to row click events */
handleRowClick?: (row: any) => void;
}

/**
* Simple pipe that allows providing a custom formatter function,
* used to modify cell values in a pure way
*/
@Pipe({
standalone: true,
name: 'formatValue',
})
export class FormatValuePipe implements PipeTransform {
transform<T, U = T>(value: T, formatter: (v: T) => U) {
if (!formatter) return value;
return formatter(value);
}
}

/**
* The `picsa-data-table` component is a lightweight wrapper around `mat-table`, used
* to simplify display of basic tables.
*
* By default the table has support for sort, pagination and data search (filter)
* @example
* ```
* <picsa-data-table [data]="myData"></picsa-data-table>
* ```
* The table has support for sort, pagination and data search (filter),
* enabled by default and configurable by an options input @see IDataTableOptions
* @example
* ```
* <picsa-data-table [data]="myData" [options]="{search:false}"></picsa-data-table>
* ```
* The table will display all cell values directly, without any additional formatting
* If needing to render values within a custom template this can be done via `valueTemplates`
* @example
* ```
* <picsa-data-table [data]="myData" [valueTemplates]={col1:col1Template}>
* <ng-template #col1Template let-value>
* <span class='some-custom-class'>{{value | modifierPipe}}</span>
* </ng-template>
* </picsa-data-table>
* ```
*
* For more advanced use cases such as custom column display prefer to directly use `mat-table`
*/
Expand All @@ -38,6 +84,7 @@ export interface IDataTableOptions {
standalone: true,
imports: [
CommonModule,
FormatValuePipe,
MatButtonModule,
MatFormFieldModule,
MatIconModule,
Expand All @@ -56,6 +103,13 @@ export class PicsaDataTableComponent implements OnChanges {
/** User option overrides */
@Input() options: IDataTableOptions = {};

/**
* Optional <ng-template> references to display specific column values in a custom template,
* indexed by column name. E.g. `{colA: myCustomTemplate}`
* https://angular.io/guide/content-projection#conditional-content-projection
*/
@Input() valueTemplates: Record<string, TemplateRef<{ $implicit: any }>> = {};

@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;

Expand All @@ -66,6 +120,7 @@ export class PicsaDataTableComponent implements OnChanges {
search: true,
sort: true,
handleRowClick: () => null,
formatHeader: (v) => v.split('_').map(capitalise).join(' '),
};

public dataSource: MatTableDataSource<any>;
Expand Down
6 changes: 6 additions & 0 deletions libs/utils/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,9 @@ export function jsonNestedProperty<T>(obj: any, nestedPath: string) {
export function base64ToBlob(base64String, mimetype: string) {
return createBlobFromBase64(base64String, mimetype);
}

/** Capitalise the first letter of a string */
export function capitalise(str: string) {
if (typeof str !== 'string') return str;
return str.charAt(0).toUpperCase() + str.slice(1);
}

0 comments on commit 3103130

Please sign in to comment.