Angular 自定义管道:解析承诺将不起作用,但调用 AsyncPipe 会

Angular Custom Pipe : Resolving promises won't work but calling AsyncPipe will

我想要实现的目标是拥有一个可以处理以下签名的管道:

any | (() => any | Promise<any>)

所以我写了下面的管道

public transform(varOrFunc: any | (() => any | Promise<any>)): any {
    if (typeof varOrFunc === 'function') {
        const result = varOrFunc();
        if (result.then) {
            return this.asyncPipe.transform(result);
        } else {
            return result;
        }
    } else {
        return varOrFunc;
    }
}

--

<my-cmp [isDisabled]="myVarOrFunc | varOrFunc></my-cmp> 

不幸的是,这没有按预期工作。

但是

public transform(varOrFunc: any | (() => any | Promise<any>)): any {
    if (typeof varOrFunc === 'function') {
        const result = varOrFunc();
        if (result.then) {
            return result;
        } else {
            return Promise.resolve(result);
        }
    } else {
        return Promise.resolve(varOrFunc);
    }
}

--

<my-cmp [isDisabled]="myVarOrFunc | varOrFunc | async ></my-cmp> 

工作得很好。

你知道为什么吗?

这是一个working demo of the problem

您不能使用 AsyncPipe 同步转换异步结果。异步管道的工作方式是存储承诺并在解决时触发更改检测,然后缓存结果。所以异步管道的转换方法在第一次调用时总是return null。

我认为实现您的目标的最佳方法是在您的管道中重新实现一些异步管道逻辑:

  • 您的管道需要不纯,因此 angular 不会缓存其结果,这意味着您必须在管道中进行缓存以避免性能问题。
  • 当承诺解决时,使用 ChangeDetectorRef 触发 detectChange。

例如:

@Pipe({ name: "varOrFuncAsync", pure: false })
export class VarOrFuncAsyncPipe implements PipeTransform {

  private lastResult: any = null;
  private lastParam:  any | (() => any | Promise<any>) = null;

  constructor(private _ref: ChangeDetectorRef) {}

  public transform(varOrFunc: any | (() => any | Promise<any>)): any {
    if (this.lastParam === varOrFunc) {
      return this.lastResult;
    }
    this.lastParam = varOrFunc;

    if (typeof varOrFunc === "function") {
      const result = varOrFunc();
      if (result.then) {
        this.lastResult = null;
        result.then(r => this.updateResult(r));
        return null;
      } else {
        this.lastResult = result;
        return result;
      }
    } else {
      this.lastResult = varOrFunc;
      return varOrFunc;
    }
  }

  updateResult(result: any) {
    this.lastResult = result;
    this._ref.detectChanges();
  }
}