以编程方式使用 AsyncPipe - Angular 2
Using AsyncPipe programmatically - Angular 2
我正在使用一个组件来呈现 table 数据,我给它列列表、数据、数据如何映射到每一列,最后是要应用的管道列表每列数据。
到目前为止一切顺利,唯一的问题是当这些管道之一是异步管道时...
经过一段时间的试验,我发现当在模板上使用asyncpipe时,转换方法被调用了好几次。但是,如果我以编程方式使用它,转换方法只会被调用一次(我调用它的时间)。
我猜它在模板上被多次调用的原因是因为它是一个不纯的管道,但我如何以编程方式处理它?
下面是 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 执行 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);
}
}
没有任何成功:(
经过一番折腾,总算得到了一些结果,这是我不得不做的,以备日后参考。
第一种方法:
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),因为 finally 似乎 return 一个新对象,而不是修改现有对象。
和 finally 之后的 repeat(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 两次,以解包值。
无论如何,希望它能真正帮助那些对以编程方式处理非纯管道感到沮丧的人!
仍然欢迎任何建议或更好的答案!
我正在使用一个组件来呈现 table 数据,我给它列列表、数据、数据如何映射到每一列,最后是要应用的管道列表每列数据。
到目前为止一切顺利,唯一的问题是当这些管道之一是异步管道时...
经过一段时间的试验,我发现当在模板上使用asyncpipe时,转换方法被调用了好几次。但是,如果我以编程方式使用它,转换方法只会被调用一次(我调用它的时间)。
我猜它在模板上被多次调用的原因是因为它是一个不纯的管道,但我如何以编程方式处理它?
下面是 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 执行 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);
}
}
没有任何成功:(
经过一番折腾,总算得到了一些结果,这是我不得不做的,以备日后参考。
第一种方法: 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),因为 finally 似乎 return 一个新对象,而不是修改现有对象。 和 finally 之后的 repeat(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 两次,以解包值。
无论如何,希望它能真正帮助那些对以编程方式处理非纯管道感到沮丧的人!
仍然欢迎任何建议或更好的答案!