Angular 5:如何在SwitchMap内订阅内部Observable监听Bookings变化?
解决思路:监听内部Observable的Bookings变更
你当前代码里用了switchMap的结果选择器参数(第二个回调函数),这个参数只会在bookingService.findByPropertyId返回的Observable第一次发射值时执行,完全不会监听它后续的变更——这就是你没法响应Bookings集合变化的核心原因。下面给你几个实用的解决方案:
方案1:单独订阅Bookings流(简单直接)
如果想同时保留对Job的处理逻辑,又要独立监听Bookings的所有更新,可以在switchMap内部单独订阅Bookings流,记得要管理订阅避免内存泄漏:
// 先在组件里定义订阅变量,用于销毁时取消 jobSubscription: Subscription; bookingsSubscription: Subscription; getDates() { if (this.booking.id) { this.jobSubscription = this.jobService.findById(this.booking.id) .map(job => job[0]) .switchMap((job) => { if (!job) { return Observable.empty(); } // 单独订阅Bookings流,捕获所有后续变更 this.bookingsSubscription = this.bookingService.findByPropertyId(job.property.id) .subscribe((bookings: Booking[]) => { this.mark_jobs_unavailable(job, bookings); this.bookings = bookings; // 把最新的Bookings存到组件变量里 }); // 返回Job流给外层订阅 return Observable.of(job); }) .subscribe(job => { this.bookingJob = job; }); } } // 组件销毁时一定要取消订阅,防止内存泄漏 ngOnDestroy() { this.jobSubscription?.unsubscribe(); this.bookingsSubscription?.unsubscribe(); }
方案2:合并流,同时获取Job和Bookings的最新值
如果希望在同一个订阅回调里同时拿到Job和Bookings的更新,可以用combineLatest把Job的常量流和Bookings流合并,这样每次Bookings变化时都会触发回调:
subscription: Subscription; getDates() { if (this.booking.id) { this.subscription = this.jobService.findById(this.booking.id) .map(job => job[0]) .switchMap((job) => { if (!job) { return Observable.empty(); } // 把Job包装成持续发射同一值的流,和Bookings流合并 const job$ = Observable.of(job); const bookings$ = this.bookingService.findByPropertyId(job.property.id); // 任意一个流发射值时,都会返回两者的最新组合 return Observable.combineLatest(job$, bookings$); }) .subscribe(([job, bookings]) => { this.bookingJob = job; this.mark_jobs_unavailable(job, bookings); this.bookings = bookings; // 响应Bookings的所有变更 }); } } ngOnDestroy() { this.subscription?.unsubscribe(); }
方案3:Angular场景下用AsyncPipe自动管理订阅
如果你是在Angular项目里,可以把Bookings流暴露为组件属性,用async管道让Angular自动处理订阅和取消:
// 组件内定义公共的Bookings流 bookings$: Observable<Booking[]>; getDates() { if (this.booking.id) { this.jobService.findById(this.booking.id) .map(job => job[0]) .subscribe(job => { this.bookingJob = job; if (job) { // 赋值Bookings流,同时用do操作处理业务逻辑 this.bookings$ = this.bookingService.findByPropertyId(job.property.id) .do(bookings => this.mark_jobs_unavailable(job, bookings)); } else { this.bookings$ = Observable.empty(); } }); } }
然后在模板里直接用async渲染:
<div *ngFor="let booking of bookings$ | async"> <!-- 这里渲染每个Booking的内容 --> </div>
为什么原来的代码不行?
switchMap的第二个参数是结果选择器,它的设计初衷是把外部Observable和内部Observable的第一次发射值合并处理,不会跟踪内部Observable的后续发射。如果findByPropertyId返回的是一个热流(比如来自Subject的实时更新),结果选择器完全捕获不到这些后续变化——而上面的方案都是直接订阅或合并这个流,自然能拿到所有的Bookings更新。
内容的提问来源于stack exchange,提问作者Subhan
相关产品推荐
相关产品推荐

