如何用RxJS的combineLatest条件展示组件且避免空值错误?
问题:combineLatest校验非空后组件仍收到null输入
我希望仅当两个Observable均未发出null或undefined时,才在模板中展示组件。使用RxJS的combineLatest运算符简化条件渲染,但仍遇到错误:TypeError: Cannot read properties of null (reading 'username')。
明明combineLatest已经校验非空值并发出true,组件却仍以null变量初始化。嵌套ngIf方案可行,但想通过combineLatest简化实现。
父组件代码
this.cardAd$ = this._cardAdService.getCardAd(this.id); this.cardOwner$ = this.cardAd$.pipe( switchMap((cardAd: CardAdInfo) => this._userService.getUserInfo(cardAd.userCard.userId)) ); this.isInfoBarVisible$ = combineLatest([this.cardAd$, this.cardOwner$]).pipe( map(([cardAd, cardOwner]) => !!cardAd && !!cardOwner) );
<app-offer-create-info-bar *ngIf="isInfoBarVisible$ | async" [cardAd]="(cardAd$ | async)!" [cardOwner]="(cardOwner$ | async)!"> </app-offer-create-info-bar>
子组件代码
<div> {{ cardOwner.username }}{{ cardAd.name }} </div>
export class OfferCreateInfoBarComponent implements OnInit { @Input({ required: true }) cardAd!: CardAdInfo; @Input({ required: true }) cardOwner!: UserInfo; ngOnInit() { console.log('cardAd', this.cardAd); // null console.log('cardOwner', this.cardOwner); // null } }
原因分析
- 每个
async管道都是独立的订阅:isInfoBarVisible$ | async的订阅和另外两个async管道的订阅没有同步机制,即使前者发出true,后两者可能因流的时序问题,在这一刻仍返回null值。 - 非空断言
!只是编译时的语法提示,无法保证运行时的实际值非空,当流短暂发出null时,依然会触发报错。
解决方案
将两个Observable合并为一个包含非空数据的单一流,确保模板只订阅一次,数据同步性得到保障:
父组件修改
this.cardAd$ = this._cardAdService.getCardAd(this.id); this.cardOwner$ = this.cardAd$.pipe( switchMap((cardAd: CardAdInfo) => this._userService.getUserInfo(cardAd.userCard.userId)) ); // 合并流并过滤非空数据 this.infoBarData$ = combineLatest([this.cardAd$, this.cardOwner$]).pipe( filter(([cardAd, cardOwner]) => !!cardAd && !!cardOwner), map(([cardAd, cardOwner]) => ({ cardAd, cardOwner })) );
模板修改
<app-offer-create-info-bar *ngIf="infoBarData$ | async as data" [cardAd]="data.cardAd" [cardOwner]="data.cardOwner"> </app-offer-create-info-bar>
这样做的优势:
- 仅一次订阅,避免多订阅导致的数据不一致
filter操作符确保只有两个数据都非空时才会发出值,模板渲染时data必然包含有效数据- 子组件实例化时会直接收到非空输入,
ngOnInit打印的内容也会是有效值
内容的提问来源于stack exchange,提问作者Ryley38
相关产品推荐
相关产品推荐

