我的应用程序组件正在对商店选择进行订阅。我将ChangeDetectionStrategy
设置为OnPush
。
我一直在阅读这是如何工作的。需要更新对象引用以触发更改。
但是,当您使用异步管道时,Angular期望可以观察到新的变化并为您执行MarkForCheck。
因此,为什么在订阅被触发并将MarkForCheck
设置为新的可观察通道数组后,我的代码为什么不呈现通道(除非我调用channels$
)。
@Component({
selector: 'podcast-search',
changeDetection: ChangeDetectionStrategy.OnPush, // turn this off if you want everything handled by NGRX. No watches. NgModel wont work
template: `
<h1>Podcasts Search</h1>
<div>
<input name="searchValue" type="text" [(ngModel)]="searchValue" ><button type="submit" (click)="doSearch()">Search</button>
</div>
<hr>
<div *ngIf="showChannels">
<h2>Found the following channels</h2>
<div *ngFor="let channel of channels$ | async" (click)="loadChannel( channel )">{{channel.trackName}}</div>
</div>
`,
})
export class PodcastSearchComponent implements OnInit {
channels$: Observable<Channel[]>;
searchValue: string;
showChannels = false;
test: Channel;
constructor(
@Inject( Store) private store: Store<fromStore.PodcastsState>,
@Inject( ChangeDetectorRef ) private ref: ChangeDetectorRef,
) {}
ngOnInit() {
this.store.select( fromStore.getAllChannels ).subscribe( channels =>{
if ( channels.length ) {
console.log('channels', !!channels.length, channels);
this.channels$ = of ( channels );
this.showChannels = !!channels.length;
this.ref.markForCheck();
}
} );
}
我尝试了多种解决方案,包括使用subject
和调用next
,但是除非我调用MarkForCheck,否则这是行不通的。
有人可以告诉我如何避免打电话给markForCheck
吗?
答案 0 :(得分:2)
这可能很难解释,但我会尽力而为。当原始的Observable(商店)发出时,它并不绑定到模板。由于您使用的是OnPush更改检测,因此,由于缺少绑定,因此该可观察到的值发出时不会将组件标记为更改。
您正试图通过覆盖组件属性来触发更改标记。即使您要在组件属性本身上创建新引用,也不会将组件标记为更改,因为组件正在更改其自身的属性,而不是在组件上推入的新值。 / p>
您认为在发出新值时异步管道将组件标记为更改是正确的。您可以在以下Angular源代码中看到它:https://github.com/angular/angular/blob/6.0.9/packages/common/src/pipes/async_pipe.ts#L139
不过,您会注意到,只有在async
管道中使用的值(称为async
)与this._obj
(即{ {1}}管道已经记录为正在发射的Observable。
由于您正在执行async
,channels$ = <new Observable>
实际上是不正确的,因为您正在更改对象引用。这就是为什么您的组件未标记为更改的原因。
您也可以在我放在一起的Stackblitz中看到这一点。第一个组件将覆盖传递给async === this._obj
管道的Observable,而第二个组件则不会覆盖它,并通过响应发出的更改来更新数据-这是您想要做的:
https://stackblitz.com/edit/angular-pgk4pw(我使用async
是因为它是模拟第三方未绑定Observable来源的简便方法。使用输出绑定(例如点击更新)更难以设置,因为在同一组件中完成操作,输出操作将触发更改标记。
一切都不会为您丢失-我建议您改为timer
。 this.channels$ = this.store.select(...)
管道为您处理async
。如果您使用的是.subscribe
管道,则无论如何都不应该使用async
。
.subscribe
请注意,您也可以将this.channels$ = this.store.select(fromStore.getAllChannels).pipe(
filter(channels => channels.length),
// channels.length should always be truthy at this point
tap(channels => console.log('channels', !!channels.length, channels),
);
与异步管道一起使用,这将消除对ngIf
的需求。