Angular 5.2.9切换Firestore查询状态后订阅变量未更新
解决Angular 5.2.9 + AngularFire Firestore切换status无数据更新的问题
我之前也踩过类似的坑,结合Angular 5和AngularFire的特性,给你梳理几个最可能的原因和解决办法:
一、先排查服务里的查询函数写法
最常见的问题就是服务函数缓存了旧的Observable,导致切换status后不会重新发起查询。比如你要是这么写:
// 错误示范:缓存了第一次的查询Observable,后续传不同status也没用 private cached$: Observable<any[]>; getItemsByStatus(status: string) { if (!this.cached$) { this.cached$ = this.afs.collection('items', ref => ref.where('status', '==', status)).valueChanges(); } return this.cached$; }
这种写法第一次调用后就把Observable存起来了,后面再传"Complete"也只会返回之前"Active"的查询结果。正确的做法是每次调用都创建新的查询Observable:
// 正确写法:每次调用都生成新的Firestore查询 getItemsByStatus(status: string) { return this.afs.collection('items', ref => ref.where('status', '==', status)).valueChanges(); }
二、组件里要正确处理Observable的切换
用async管道(推荐)
如果组件里是直接把Observable赋值给变量,一定要在模板里用async管道,它会自动帮你订阅新的Observable:
// 组件代码 items$: Observable<any[]>; ngOnInit() { this.items$ = this.loadService.getItemsByStatus('Active'); } // 切换status时重新赋值Observable onStatusChange(status: string) { this.items$ = this.loadService.getItemsByStatus(status); }
模板里必须加async:
<div *ngFor="let item of items$ | async"> {{ item.name }} </div>
手动订阅的话要记得取消旧订阅
如果你是手动订阅Observable,切换status时一定要先取消之前的订阅,不然旧的订阅可能会干扰新的结果,还会造成内存泄漏:
// 组件代码 private itemSubscription: Subscription; items: any[] = []; ngOnInit() { this.loadItems('Active'); } loadItems(status: string) { // 先取消旧的订阅 if (this.itemSubscription) { this.itemSubscription.unsubscribe(); } // 订阅新的查询结果 this.itemSubscription = this.loadService.getItemsByStatus(status).subscribe(items => { this.items = items; }); } onStatusChange(status: string) { this.loadItems(status); } // 组件销毁时一定要清理订阅 ngOnDestroy() { if (this.itemSubscription) { this.itemSubscription.unsubscribe(); } }
三、检查Firestore本身的问题
- 确认数据存在且大小写正确:Firestore的查询是严格区分大小写的,你要检查数据库里的文档是不是真的有
status字段值为"Complete"(注意首字母大写),别写成"complete"或者其他拼写了。 - 复合查询需要索引:如果你的查询除了status还有其他where条件,Firestore可能需要你创建复合索引。打开浏览器控制台看看有没有报错,要是有索引相关的错误,跟着提示去Firestore控制台创建索引就行。
四、用BehaviorSubject做响应式状态管理(更灵活的方案)
如果你的组件需要频繁切换状态,推荐用BehaviorSubject来实现响应式的查询切换,这样状态一变,数据自动更新:
服务里的代码:
import { BehaviorSubject } from 'rxjs'; import { switchMap } from 'rxjs/operators'; // 用BehaviorSubject保存当前的status,默认值设为"Active" private statusSubject = new BehaviorSubject<string>('Active'); // 对外暴露可观察的status(只读) currentStatus$ = this.statusSubject.asObservable(); // 根据当前status自动返回对应的查询结果 items$ = this.currentStatus$.pipe( switchMap(status => { return this.afs.collection('items', ref => ref.where('status', '==', status)).valueChanges(); }) ); // 提供切换status的方法 updateStatus(status: string) { this.statusSubject.next(status); }
组件里的代码:
items$: Observable<any[]>; ngOnInit() { // 直接订阅服务里的items$ this.items$ = this.loadService.items$; } // 切换status时调用服务的方法就行 onStatusChange(status: string) { this.loadService.updateStatus(status); }
模板还是用async管道,这样每次切换status,items$都会自动更新为新的查询结果,非常省心。
内容的提问来源于stack exchange,提问作者Noober
相关产品推荐
相关产品推荐

