【问题标题】:Dynamic pipe in Angular 2Angular 2中的动态管道
【发布时间】:2016-08-02 13:49:45
【问题描述】:

我正在尝试创建一个组件,您可以在其中传递应该用于组件内列表的管道。通过测试和四处寻找答案,我发现唯一的解决方案似乎是:

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

my-component模板:

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

然后将myFilter 映射到正确的管道逻辑并运行它,但这似乎有点脏并且不是最佳的。

我认为自 Angular 1 以来他们会想出一个更好的解决方案来解决这个问题,你也可以按照这些思路做一些事情。

在 Angular 2 中没有更好的方法吗?

【问题讨论】:

  • getpipe 是您的自定义过滤器吗?
  • 不仅可以在 AngularJS 中执行此操作,还可以在 Angular +2 中执行此操作。该技术类似于下面@Balu 发布的技术。

标签: angular angular2-pipe


【解决方案1】:

基于 borislemke 的回答,这是一个不需要 eval() 的解决方案,我觉得它相当干净:

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

【讨论】:

  • 我无法让它工作。我收到以下错误Error: StaticInjectorError(AppModule)[date]: StaticInjectorError(Platform: core)[date]: NullInjectorError: No provider for date!
  • @Chuck 您是否将DatePipe 添加到您的应用模块的提供程序中?
  • 我所有的管道都是通过 app.module 中提供的 shared.module 导出的。当我按照上面的方式实现代码时,它就可以工作了。但是我在我的代码中将 myPipe 设置为 @Input() pipe 以便可以通用
  • @Chuck 如果你想出了答案,请评论或添加你所做的答案。其他人也想知道。
  • 在对它应用展开 (...) 之前,我必须对 pipeArgs 添加一个检查以确保它是一个数组,否则它不会为我的非动态管道提供 args。 if (Array.isArray(pipeArgs)) { return pipe.transform(value, ...pipeArgs); } else { return pipe.transform(value, pipeArgs); } 它曾经在带有 Typescript 3.1.6 的 Angular 7 中运行良好,但自从使用 Typescript 3.5.3 升级到 Angular 8 后,我遇到了问题。
【解决方案2】:

我设法让一些东西工作,它有点肮脏和邪恶(使用 eval)但它对我有用。就我而言,我有一个表格组件,每一行都有不同的数据类型(例如标题、网址、日期、状态)。在我的数据库中,状态标记为1enabled0disabled。当然,最好向我的用户显示启用/禁用。另外,我的标题栏是多语言的,这使它成为一个以enid 为键的对象。

// Example row object:
title: {
    "en": "Some title in English",
    "id": "Some title in Indonesian"
},
status: 1 // either 1 or 0

理想情况下,我需要 2 个不同的管道来转换我的数据以显示给我的应用程序的用户。像translateTitlegetStatus 这样的东西就可以了。让我们调用父管道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]
    }
}

我可能会对使用 eval 感到非常讨厌。希望对您有所帮助!

更新:当你可能需要它时

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>

将返回:

| title          | url            | date           | status         |
| Some post t...   a-beautiful...   an hour ago      enabled
| Some post ...2   a-beautifu...2   2 days ago       disabled

【讨论】:

    【解决方案3】:

    解决此问题的最简单方法是不在 HTML 模板中使用管道,而是将管道注入组件的构造函数(使用 DI),然后在功能上应用转换。这适用于 Observable 地图或类似的 rxjs 流。

    【讨论】:

    • 好建议,但是如果要按我想要的方式使用它仍然需要包装服务。
    【解决方案4】:

    不幸的是,我不这么认为。这与 angular1 中的相同,其中您有一个函数为您想要的动态管道返回一个字符串。

    查看文档,这正是他们展示的方式。

    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>
    `
    

    然后在控制器中:

    get format()   { return this.toggle ? 'shortDate' : 'fullDate'}
    

    唉,情况可能更糟! :)

    【讨论】:

      【解决方案5】:

      在 @Balu 的基础上回答这个我必须做的事情才能让它与 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;
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        我通过将管道提供程序发送到组件并运行转换方法来处理这个问题。它适用于 Angular 9。我希望它对某人有所帮助!演示:https://stackblitz.com/edit/angular-kdqc5e

        pipe-in​​jector.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);
              }
            }
        

        应用组件.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 { }
        

        【讨论】:

          猜你喜欢
          • 2016-08-24
          • 2017-01-16
          • 1970-01-01
          • 2017-03-11
          • 2023-03-25
          • 1970-01-01
          • 1970-01-01
          • 2016-12-06
          相关资源
          最近更新 更多