以编程方式使用AsyncPipe - Angular 2

时间:2017-07-25 08:18:44

标签: angular angular-pipe

我正在使用一个组件来呈现像数据一样的表,我给它列了列,数据,数据如何映射到每一列,最后是一个管道列表应用于每个列数据。

到目前为止一切顺利,唯一的问题是当其中一个管道是异步管道时......

经过一段时间的实验,我发现当在模板上使用asyncpipe时,会多次调用transform方法。但是,如果我以编程方式使用它,则只调用一次transform变换方法(我调用它的时间)。

我猜它在模板上被多次调用的原因是因为它是一个不纯的管道,但我怎么能以编程方式处理呢?

这是一个plunker,展示了我刚才所说的内容:

@Injectable()
export class AsyncProvider {
  constructor() {}

  getById(id: number) {
    // Just some mock data
    return Observable.of({id, times_five: id*5}).delay(1000);
  }
}

@Component({
  selector: 'my-app',
  providers: [AsyncPipe, AsyncProvider]
  template: `
    <div>
      <p>Template async pipe</p>
      <p>{{ asyncObj | async | json }}</p>
      <hr>
      <p>Programmatically async pipe</p>
      <p>{{ asyncObjPiped | json }}</p>
    </div>
  `,
  directives: []
})
export class App {
  constructor(
    private _provider: AsyncProvider,
    private _async: AsyncPipe
  ) {
    this.asyncObj = this._provider.getById(123);
    this.asyncObjPiped = this._async.transform(this.asyncObj);
  }
}

修改 因为AsyncPipe在接收新值的ChangeDetectorRef uppon上执行markForCheck(),所以我也尝试了以下内容:

export class App {
  constructor(
    private _provider: AsyncProvider,
    private _async: AsyncPipe,
    private _ref: ChangeDetectorRef,
  ) {
    this.asyncObj = this._provider.getById(123);
    this.asyncObjPiped = this._async.transform(this.asyncObj);

    setInterval(() => {
      this._ref.detectChanges();
    }, 1000);
  }
}

没有任何成功:(

1 个答案:

答案 0 :(得分:1)

经过一番挣扎后,我设法得到了一些结果,这是我必须做的,以备将来参考。

第一种方法: plunker

export class App {
  constructor(
    private _provider: AsyncProvider,
    private _async: AsyncPipe,
  ) {

    this.asyncObj = this._provider.getById(123)

    let processPipe = () => {
      this.asyncObjPiped = this._async.transform(new_asyncObj);
    }
    let new_asyncObj = this.asyncObj.finally(processPipe).repeat(2);
    processPipe();
  }
}

请注意,需要一个新变量(new_asyncObj),因为最终似乎返回一个新对象而不是修改现有对象。 并且在最后的重复(2)之后它将解开承诺。

第二种方法: plunker

export class App {
  constructor(
    private _provider: AsyncProvider,
    private _async: AsyncPipe,
  ) {

    this.asyncObj = this._provider.getById(123)

    setInterval(() => {
      this.asyncObjPiped = this._async.transform(this.asyncObj);
    }, 500);

  }
}

每500毫秒重新计算一次管道,简单有效,即使我期待更好的东西。

最终方法: plunker

export class App {
  constructor(
    private _provider: AsyncProvider,
    private _async: AsyncPipe,
    private _ref: ChangeDetectorRef,
    private zone: NgZone,
  ) {

    this.asyncObj = this._provider.getById(123)

    this.zone.onMicrotaskEmpty
      .subscribe(() => {
        this.asyncObjPiped = this._async.transform(this.asyncObj);
        this.asyncObjPiped = this._async.transform(this.asyncObj);
        this._ref.detectChanges();
      });
  }
}

使用NgZone和ChangeDetectorRef似乎也起作用,甚至认为一些丑陋的黑客攻击AsyncPipe两次,以打开该值。

无论如何,希望它能真正帮助那些以编程方式处理非纯管道而感到沮丧的人!

任何建议或更好的答案仍然欢迎!