【问题标题】:invoke pipe during run time using pipe name /metadata在运行时使用管道名称 /metadata 调用管道
【发布时间】:2017-01-08 05:47:02
【问题描述】:

我正在尝试构建一个动态表,我想在 运行时 中决定使用哪个管道(如果有)。

我正在尝试实现类似于(简化):

export class CellModel {
     public content: any;
     public pipe: string
}

表格

<tbody>
     <tr *ngFor="let row of data">
         <template ngFor let-cell [ngForOf]=row>
           <td *ngIf="cell.pipe">{{cell.content | cell.pipe}}</td>
           <td *ngIf="!cell.pipe">{{cell.content}}</td>
     </tr>
</tbody>

我知道这个例子给出了一个错误。我可以使用 Reflect 是某种方式或其他解决方案吗?

【问题讨论】:

    标签: angular angular2-pipe


    【解决方案1】:

    您不能动态应用管道。您可以做的是构建一个“元”管道来决定要进行哪些转换。

    @Pipe({
      name: 'meta'
    })
    class MetaPipe implements PipeTransform {
      transform(val, pipes:any[]) {
        var result = val;
        for(var pipe of pipes) {
          result = pipe.transform(result);
        }
        return result;
      }
    }
    

    然后像这样使用它

    <td *ngIf="cell.pipe">{{cell.content | meta:[cell.pipe]}}</td>
    

    【讨论】:

    • 谢谢@Gunter。很好的答案。有一个小问题。 cell.pipe' is a type of string - the name of the pipe in the @Pipe` 元数据。所以在 MetaPipe 中,我如何使用 Reflect(或更好的东西)来根据名称注入正确的 pipe
    • 您可以像{provide: 'pipes', useFactory: (pipe1, pipe2) =&gt; {pipe1Name: pipe1, pipe2Name: pipe2}, deps: [Pipe1, Pipe2]} 一样提供一个对象(从名称映射到管道),然后将其注入元管道constructor(@Inject('pipes') private pipes) {}; 并像result = this.pipes[pipe].transform(result); 一样使用它
    • 您的解决方案效果很好,但我已经通过管道注入做到了。我现在正在尝试使用 PipesProvider 解决方案。我找不到任何示例,其中useFactory 是标签和类之间的映射。无论我在做什么,我都会收到错误“ts7028 未使用的标签”。能否请您添加一个更具体的示例
    • 请创建一个新问题,在其中发布代码,展示您尝试完成的工作以及失败的地方。
    【解决方案2】:

    仅对于运行时编译,您可以创建一个动态编译模板的指令。

    更新:

    compileModuleAndAllComponentsAsync 用于RC.6^

    dynamic-pipe.ts

      ngAfterViewInit() {
        const data = this.data.content;
        const pipe = this.data.pipe;
    
        @Component({
          selector: 'dynamic-comp',
          template: '{{ data | ' + pipe  + '}}'
        })
        class DynamicComponent  {
            @Input() public data: any;
        };
    
        @NgModule({
          imports: [BrowserModule],
          declarations: [DynamicComponent]
        })
        class DynamicModule {}
    
        this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
          .then(({moduleFactory, componentFactories}) => {
            const compFactory = componentFactories.find(x => x.componentType === DynamicComponent);
            const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
            const cmpRef = this.vcRef.createComponent(compFactory, 0, injector, []);
            cmpRef.instance.data = data;
          });
      }
    

    Plunker sample RC.6^


    过时的解决方案

    RC.5 中,您可以使用 Compiler.compileComponentSync/Async 来做到这一点:

    dynamic-pipe.ts

    @Directive({
      selector: 'dynamic-pipe' 
    })
    export class DynamicPipe {
      @Input() data: CellModel;
    
      constructor(private vcRef: ViewContainerRef, private compiler: Compiler) {}
    
      ngAfterViewInit() {
        const metadata = new ComponentMetadata({
          template: '{{ data | ' + this.data.pipe  + '}}'
        });
    
        const data = this.data.content;
        const decoratedCmp = Component(metadata)(class DynamicComponent {  data = data; });
    
        this.compiler.compileComponentAsync(decoratedCmp)
          .then(factory => {
            const injector = ReflectiveInjector.fromResolvedProviders([], 
               this.vcRef.parentInjector);
            this.vcRef.createComponent(factory, 0, injector, []);
          });
      }
    }
    

    并以这种方式使用它:

    <template ngFor let-cell [ngForOf]="row">
       <td><dynamic-pipe [data]="cell"></dynamic-pipe></td>
    </template>
    

    另请参阅演示此功能的 plunker sample RC.5

    无论如何,我认为 Günter 的解决方案更可取

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-29
    • 2020-06-02
    • 2022-01-26
    • 2018-06-07
    • 2020-11-01
    • 2021-09-13
    • 2015-07-15
    • 1970-01-01
    相关资源
    最近更新 更多