The subject
I'm building an abstract table component to which I pass what pipe it should use in certain columns. As the data passed may vary, so the pipes should vary as well.
The goal
To use whatever pipe is passed to the table
The project
Here's how it should look like in html in my opinion
<!-- html -->
<span *ngIf="element.pipe">{{ row[element.column] | <<here_inject_an_appropriate_pipe>> }}</span>
The column settings are passed through an object and have form of
//typescript
columnSettings: [
...
{column: 'fruitExpDate', caption: 'Best before', pipe: 'date: \"' + PIPE_DATE_FORMAT + '\"' },
...
]
and PIPE_DATE_FORMAT
holds the string 'yyyy-MM-dd'
What I tried
<!-- html -->
<span *ngIf="element.pipe">{{ row[element.column] | element.pipe }}</span>
@Pipe({
name: 'dynamicPipe',
})
export class DynamicPipe implements PipeTransform {
// constructor(private abstractTableService: AbstractTableService) {}
transform(value: any, pipe: string): any {
const pipeToken: any = pipe.split(':')[0].replace(/[\s]+/g, '');
const pipeArgs: any = pipe.split(':')[1].replace(/[\s]+/g, '');
console.log(value);
console.log(pipe);
// return pipeToken.transform(value, ...pipeArgs);
return 'check pipe';
}
}
and here I tried many different things to call the requested pipe but eventually didn't figure out how to do this. Here's my html with the custom pipe:
<!-- html -->
<span *ngIf="element.pipe">{{ row[element.column] | dynamicPipe: element.pipe }}</span>
@Injectable()
export class AbstractTableService {
constructor(
private date: DatePipe,
) {}
getDatePipe(): DatePipe {
return this.date;
}
}
but here I had no idea how to use this service effectively.
You use data binding with a pipe to display values and respond to user actions. If the data is a primitive input value, such as String or Number, or an object reference as input, such as Date or Array, Angular executes the pipe whenever it detects a change for the input value or reference.
Angular contains built-in pipes for data transformations. The following are commonly used built-in pipes for data formatting: DatePipe: Formats a date value according to locale rules. UpperCasePipe: It transforms text to all upper case. LowerCasePipe: It transforms text to all lower case. CurrencyPipe: Transforms a number to a currency string.
A pure pipe must use a pure function, which is one that processes inputs and returns values without side effects. In other words, given the same input, a pure function should always return the same output. With a pure pipe, Angular ignores changes within composite objects, such as a newly added element of an existing array, because checking a ...
Prerequisites: Basic understanding of Angular Components, Directives, and the structure of an Angular application. The pipes in Angular are of two types: The built-in pipes are those which are already given to us by the Angular and are ready to use. Whereas, in some scenarios, the built-in pipes are not sufficient to fulfill our use case.
You need to create an instance of the selected pipe inside a dynamic pipe. To do that, you can utilize Angular injector. The dynamic pipe (what I call it) can be something like this:
import { Pipe, PipeTransform, Injector, Type } from '@angular/core';
@Pipe({
name: 'dynamicPipe'
})
export class DynamicPipe implements PipeTransform {
constructor(private injector: Injector) {}
transform(value: any, requiredPipe: Type<any>, pipeArgs: any): any {
const injector = Injector.create({
name: 'DynamicPipe',
parent: this.injector,
providers: [
{ provide: requiredPipe }
]
})
const pipe = injector.get(requiredPipe)
return pipe.transform(value, pipeArgs);
}
}
Make sure to pass the pipe class (type) as args not a string representation of its name. If you are going to pass a string, let's say the data comes from server-side, you might need to consider creating a mapping for that.
A fully working example can be found here: https://stackblitz.com/edit/angular-ivy-evzwnh
This is a rough implementation. I am not sure about Tree-Shaking. It needs more testing and optimization.
pipe is NOT a string, so you can't use pipe:'date: \"' + PIPE_DATE_FORMAT + '\"'
Your second aproach is closed to you want get it, but you need use a switch case
NOTE 1: From Angular 9 you can use directly the functions: formatDate, formatNumber, formatCurrency and formatPercent
import { formatDate,formatNumber,formatCurrency,formatPercent } from '@angular/common';
transform(value: any, pipe: string): any {
const pipeToken: any = pipe.split(':')[0].replace(/[\s]+/g, '');
const pipeArgs: any = pipe.split(':')[1].replace(/[\s]+/g, '');
let result=value;
switch (pipeToken)
{
case "date":
result=formatDate(value,pipeArgs) //(*)
break
case "number"
result=formatNumber(value,pipeArgs) //(*)
break
...
}
return result;
}
(*) check the docs to know how use the functions, I write "pseudo-code"
NOTE 2: perhafs if you create your "columns objects" with two properties, pipeKing and args- this one as an array-, your pipe becomes more confortable
e.g.
{
column: 'fruitExpDate',
caption: 'Best before',
pipeKind: 'date'
pipeArgs:[PIPE_DATE_FORMAT]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With