Dynamic pipe in Angular 2

匿名 (未验证) 提交于 2019-12-03 08:46:08

问题:

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 answers the only solution appears to create something like:

<my-component myFilter="sortByProperty"></my-component> 

my-component template:

<li *ngFor="#item of list | getPipe:myFilter"></li> 

Which then maps myFilter to the correct pipe logic and runs it, but this seems a bit dirty and not optimal.

I thought they would have come up with a better solution to this problem since Angular 1 where you would also do something along these lines.

Is there not a better way to do this in Angular 2?

回答1:

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! :)



回答2:

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 = []; } 


回答3:

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 


回答4:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!