创建自己异步的自定义管道

Jer*_*oen 9 angular

我创建了一个自定义管道,可以从API检索文本内容,如下所示:

@Pipe({ name: 'apiText' })
export class ApiTextPipe implements PipeTransform {
  constructor(private myApiService: MyApiService) {
  }

  transform(key: string): Observable<string> {
    return this.myApiService.getText(key);
  }
}
Run Code Online (Sandbox Code Playgroud)

我必须像这样使用它:

<strong>{{'some-key' | apiText | async}}</strong>
Run Code Online (Sandbox Code Playgroud)

但实际上,我将始终希望结合使用apiTextasync管道。我更喜欢这样写:

<strong>{{'some-key' | apiTextAsync}}</strong>
Run Code Online (Sandbox Code Playgroud)

我能以某种方式做到这一点,并通过组合两个管道使事情变得更干燥吗?

更新:我已打开GitHub问题作为对此功能的解决方案的功能要求。

max*_*dev 10

无需复制async管道转换的源,您真正需要的唯一东西是pure: false属性和ChangeDetectorRef.

这是一个最小的例子:

import { ChangeDetectorRef, Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'example',
  pure: false
})
export class ExamplePipeTransform implements PipeTransform {

  value?: string;

  constructor(private _ref: ChangeDetectorRef) {}

  transform(value: any): unknown {

    if (!this.value) {
      this.value = value;

      // Just an example for some async operation:
      setTimeout(() => {
        // In the callback do this:
        this.value = value;
        this._ref.markForCheck();
      }, 1000);
    }

    return this.value;
  }
}
Run Code Online (Sandbox Code Playgroud)

一旦您想要执行的异步操作完成,您就可以存储该值并简单地调用ChangeDetectorRef.markForCheck. 这将导致transform再次被调用。

这里setTimeout只是一个示例,您可以执行Observable.subscribe然后返回解析值。


Jer*_*oen 5

一些评论者指出(正确地如此),这违反了SRP,并可能会损害可读性。但是,即使这会让我重新考虑我是否这个,我仍然肯定想知道如何做到这一点,如果你想要它。我找到了两个解决方案(再次在评论者的帮助下)。

成分(不推荐)

创建自己的内部AsyncPipe并使用它。这是应该工作的基本设置:

@Pipe({ name: 'apiText', pure: false })
export class ApiTextPipe implements PipeTransform, OnDestroy {
  private asyncPipe: AsyncPipe;

  constructor(private myApiService: MyApiService, private injector: Injector) {
    this.asyncPipe = new AsyncPipe(injector.get(ChangeDetectorRef));
  }

  ngOnDestroy() {
     this.asyncPipe.ngOnDestroy();
  }

  transform(key: string): string {
    return this.asyncPipe.transform(this.myApiService.getText(key));
  }
}
Run Code Online (Sandbox Code Playgroud)

除了上述缺点(评论者注意到)之外,我还看到其他问题:

  • 如果AsyncPipe发生了变化(例如,它开始实现)OnInit,那么我们自己的管道也需要更改。您可能会错过这一点。我们管道的实现以这种方式耦合到AsyncPipe不好。

  • 我似乎无法AsyncPipe注射,因为它依赖于special ChangeDetectorRef,所以我使用了这种建议的方法,直接从注射器中询问它。(可能有更好的方法来执行此操作,但是我现在不进一步探讨...)

继承(不推荐)

您也可以尝试extends AsyncPipe在自己的管道中进行操作,但是该路线的代码更加笨拙,将您的管道与异步管道紧密耦合。当我尝试这种方法时,存在一些问题:

  • 您再次需要ChangeDetectorRef转接到super(...)通话
  • 您需要紧密结合到transform方法的签名AsyncPipe
  • transform得到超级复杂,因为它不再只是需要一个字符串(参见前一点)
  • 我不知道这是否正确使Angular调用超类的ngOnDestroy方法

再加上我忘记的一切。我觉得代码很讨厌,甚至共享它似乎也不明智。

复制源(不推荐)

正如评论者之一建议的那样,的来源AsyncPipe是开放的。因此,您可以采用它并以此构建自己的管道。

由于明显的原因,这不是明智的决定。


最重要的是,我现在对自己的问题的答案(尽管我很高兴地证明另一个答案是错误的!)是,这不容易实现,最好的解决方案是不解决任何问题:只需坚持使用二重奏模板。

  • 我要哭了一点:如果我使用 ```{{ 变量 | 定制管 | async }}``` 我的模板会比使用 ```customPipeAsync``` 更复杂,我有一个动态表,其中填充了一系列列,每个列都有一个管道来修改单元格值,我最好选项是将``customPipeAsync````添加到相应colls的管道中 (2认同)