I\'m trying to create a component where you can pass which pipe that should be used for a list inside the component. From what I could find by testing and looking around for
Unfortunately I don't think so. It's the same as in angular1 where you have a function return a string for the dynamic Pipe you want.
Looking at the docs that's exactly how they show it as well.
https://angular.io/docs/ts/latest/guide/pipes.html
template: `
<p>The hero's birthday is {{ birthday | date:format }}</p>
<button (click)="toggleFormat()">Toggle Format</button>
`
Then in the controller:
get format() { return this.toggle ? 'shortDate' : 'fullDate'}
Alas, it could be worse! :)
The easiest way to tackle this would be to not use the pipes in the HTML templates, but instead, inject the pipe into a component's constructor (using DI), then apply the transform functionally. This works quite well with an Observable map or similar rxjs streams.
I handled this by sending the pipe provider to the component and it runs the transform method. And It works with Angular 9. I hope it helps someone! Demo: https://stackblitz.com/edit/angular-kdqc5e
pipe-injector.component.ts:
import { Component, OnInit, Input, PipeTransform } from '@angular/core';
@Component({
selector: 'pipe-injector',
template: `
Should inject my pipe provider
{{ getText() }}
`,
providers: []
})
export class PipeInjectorComponent {
@Input() pipeProvider: PipeTransform;
@Input() pipeArgs: Array<any>;
@Input() textToFormat: string;
getText() {
return this.pipeProvider.transform(this.textToFormat, ...this.pipeArgs);
}
}
app-component.ts:
import { Component, OnInit } from '@angular/core';
import { DatePipe } from '@angular/common';
@Component({
selector: 'app-root',
template: `
<pipe-injector [pipeProvider]="pipeProvider" [pipeArgs]="pipeArgs" textToFormat='05-15-2020'>
</pipe-injector>
`,
providers: []
})
export class AppComponent implements OnInit {
pipeArgs = ['dd/MM/yyyy'];
constructor(public pipeProvider: DatePipe) {}
}
app.module.ts:
import { DatePipe } from '@angular/common';
import { PipeInjectorComponent } from './pipe-injector.component';
@NgModule({
declarations: [
PipeInjectorComponent,
],
imports: [
],
providers: [
DatePipe,
],
bootstrap: [AppComponent]
})
export class AppModule { }
Building on borislemke's answer, here's a solution which does not need eval()
and which I find rather clean:
dynamic.pipe.ts:
import {
Injector,
Pipe,
PipeTransform
} from '@angular/core';
@Pipe({
name: 'dynamicPipe'
})
export class DynamicPipe implements PipeTransform {
public constructor(private injector: Injector) {
}
transform(value: any, pipeToken: any, pipeArgs: any[]): any {
if (!pipeToken) {
return value;
}
else {
let pipe = this.injector.get(pipeToken);
return pipe.transform(value, ...pipeArgs);
}
}
}
app.module.ts:
// …
import { DynamicPipe } from './dynamic.pipe';
@NgModule({
declarations: [
// …
DynamicPipe,
],
imports: [
// …
],
providers: [
// list all pipes you would like to use
PercentPipe,
],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts:
import { Component, OnInit } from '@angular/core';
import { PercentPipe } from '@angular/common';
@Component({
selector: 'app-root',
template: `
The following should be a percentage:
{{ myPercentage | dynamicPipe: myPipe:myPipeArgs }}
`,
providers: []
})
export class AppComponent implements OnInit {
myPercentage = 0.5;
myPipe = PercentPipe;
myPipeArgs = [];
}
Building on @Balu answer this what I had to do to get it working with Angular 9
import { Injector, Pipe, PipeTransform } from '@angular/core';
import { PercentPipe, CurrencyPipe, DecimalPipe } from '@angular/common';
@Pipe({
name: 'dynamicPipe'
})
export class DynamicPipe implements PipeTransform {
public constructor(private injector: Injector, private percentPipe: PercentPipe) {
}
transform(value: any, pipeToken: any, pipeArgs: any[]): any {
const MAP = { 'currency': CurrencyPipe, 'decimal': DecimalPipe, 'percent': PercentPipe }
if (pipeToken && MAP.hasOwnProperty(pipeToken)) {
var pipeClass = MAP[pipeToken];
var pipe = this.injector.get(pipeClass);
if (Array.isArray(pipeArgs)) {
return pipe.transform(value, ...pipeArgs);
} else {
return pipe.transform(value, pipeArgs);
}
}
else {
return value;
}
}
}
I managed to get something working, it's a bit dirty and evil (with eval) but it does the trick for me. In my case, I have a table component with different data types in each row (e.g title, url, date, status). In my database, status is marked as either 1
as enabled
or 0
for disabled
. Of course, it is more preferable to be showing enabled/disabled to my user. Also, my title column is multilingual, which makes it an object with either en
or id
as it's key.
// Example row object:
title: {
"en": "Some title in English",
"id": "Some title in Indonesian"
},
status: 1 // either 1 or 0
Ideally, I need 2 different pipes to convert my data to show to my app's user. Something like translateTitle
and getStatus
will do fine. Let's call the parent's pipe dynamicPipe
.
/// some-view.html
{{ title | dynamicPipe:'translateTitle' }}
{{ status | dynamicPipe:'getStatus' }}
/// dynamic.pipe.ts
//...import Pipe and PipeTransform
@Pipe({name:'dynamicPipe'})
export class DynamicPipe implements PipeTransform {
transform(value:string, modifier:string) {
if (!modifier) return value;
return eval('this.' + modifier + '(' + value + ')')
}
getStatus(value:string|number):string {
return value ? 'enabled' : 'disabled'
}
translateTitle(value:TitleObject):string {
// defaultSystemLanguage is set to English by default
return value[defaultSystemLanguage]
}
}
I'll probably get a lot of hate on using eval. Hope it helps!
Update: when you might need it
posts = {
content: [
{
title:
{
en: "Some post title in English",
es: "Some post title in Spanish"
},
url: "a-beautiful-post",
created_at: "2016-05-15 12:21:38",
status: 1
},
{
title:
{
en: "Some post title in English 2",
es: "Some post title in Spanish 2"
},
url: "a-beautiful-post-2",
created_at: "2016-05-13 17:53:08",
status: 0
}
],
pipes: ['translateTitle', null, 'humanizeDate', 'getStatus']
}
<table>
<tr *ngFor="let row in posts">
<td *ngFor="let column in row; let i = index">{{ column | dynamicPipe:pipes[i] }}</td>
</tr>
</table>
Will return:
| title | url | date | status |
| Some post t... a-beautiful... an hour ago enabled
| Some post ...2 a-beautifu...2 2 days ago disabled