A while back, I blogged about an Angular Table component that I wrote. Since that time, this component has expanded a fair bit as my application needs grow. Once such feature that I needed recently was the ability to format data dynamically.
Formatting and filtering data are very similar in Angular. Pipes do the heavy lifting. However, unlike AngularJS, where one could pass a simple string and the framework was smart enough to figure out how to format the viewable data, Angular is type-specific and provides discrete Pipes for the various types of formatting. This creates a problem within the context of a dynamically defined table.
Consider the cases where we have columns of data that display currency and dates.
If you’ll recall, in my previous post, I defined a “CustomTableColumnDefinition” which had a “filter” property. Yes, this is poorly named and should really be a “format.” My idea was to use this in the same way that I used it in AngularJS. It was passed to an AngularJS filter.
With Angular, though, we can do something somewhat similar using the built-in Pipes, but we have to write the conditional logic for choosing the correct pipe, based on the filter, ourselves.
First thing, unless we have we have defined the Pipe providers globally, we need to change the component to reference the appropriate Pipes:
@Component({ selector: 'custom-table', templateUrl: 'templates/customTable.html', providers: [ CurrencyPipe, DatePipe, DecimalPipe, PercentPipe ], changeDetection: ChangeDetectionStrategy.OnPush })
Second, we can inject those static pipes into our table’s constructor:
constructor( @Optional() private emitter: CustomTableEmitter, private changeRef: ChangeDetectorRef, private appRef: ApplicationRef, private dataSvc: DataService, private lipsumSvc: LoremIpsumService, private currencyPipe: CurrencyPipe, private decimalPipe: DecimalPipe, private datePipe: DatePipe, private percentPipe: PercentPipe) { }
With a handle to the framework’s built-in pipes, we can execute their “transform” methods directly. The transform method applies the format.
Third, the “getCellValue” method has to be modified to apply the format using the appropriate pipe’s transform. Initially, I am formatting dates and currency. You can see in the code snippet below that I look for specific filter values on the CustomTableColumnDefinition. If those values are found, the correct pipe transform is utilized.
getCellValue(row: any, column: CustomTableColumnDefinition): string { var result: string = ''; if (column.isComputed) { let evalfunc = new Function('r', 'return ' + column.binding); result = evalfunc(row); } else { result = column.binding.split('.').reduce((prev: any, curr: string) => prev[curr], row); } if (column.filter) { if (column.filter === "currency") { result = this.currencyPipe.transform(result); } if (column.filter.indexOf("date=") !== -1) { var filter = column.filter.replace("date=", ""); result = this.datePipe.transform(result, filter); } } return result; }
Finally, the column definitions are updated with a new filter value.
var columns : CustomTableColumnDefinition[] = [ { name: 'Column 6', value: 'column6', binding: 'column6', filter: "currency", isWatched: true, style: {} }, { name: 'Column 8', value: 'column8', binding: 'column8', filter: "date=MM/dd/yyyy", style: {} }, ];
The plunk below shows everything in action.