Angular 6自定义对象数组Observable仅加载一次问题求助
问题场景
我最近在用Angular 6搭配angularfire2 firestore开发应用时,碰到了一个头疼的问题:
- 首次访问
/list路由时,对象列表能正常渲染展示 - 切换到
/Home路由后再切回/list,列表直接不显示,控制台也没有任何报错信息 - 只有手动按F5刷新页面,列表才会重新出现
原服务代码
import { AngularFirestoreCollection, AngularFirestoreDocument, AngularFirestore } from 'angularfire2/firestore'; import { Personagem } from './../model/personagem'; import { Observable } from 'rxjs/Observable'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class PersonagemService { personagens: Observable; personagemColl: AngularFirestoreCollection; personagemDoc: AngularFirestoreDocument; constructor(private afs: AngularFirestore) { this.personagemColl = this.afs.collection('personagem', ref=> ref.orderBy('nome', 'asc')); this.personagens = this.personagemColl.snapshotChanges().map(changes=> { return changes.map(p => { const data = p.payload.doc.data() as Personagem; data.id = p.payload.doc.id; return data; }); }); } getPersonagens() { return this.personagens; } addPersonagem(personagem: Personagem) { this.personagemColl.add(personagem); } deletePersonagem(personagem: Personagem) { this.getPersonagemDoc(personagem) this.personagemDoc.delete(); } updatePersonagem(personagem: Personagem) { this.getPersonagemDoc(personagem) this.personagemDoc.update(personagem); } getPersonagemDoc(personagem: Personagem) { return this.personagemDoc = this.afs.doc(`personagem/${personagem.id}`); } }
原组件代码
import { Router } from '@angular/router'; import { PersonagemService } from './../../services/personagem.service'; import { Component, OnInit } from '@angular/core'; import { Observable } from '@firebase/util'; import { Personagem } from '../../model/personagem'; @Component({ selector: 'app-listar-personagem', templateUrl: './listar-personagem.component.html', styleUrls: ['./listar-personagem.component.css'] }) export class ListarPersonagemComponent implements OnInit { personagens: Personagem[]; constructor(private personagemServ: PersonagemService, private router: Router) { } ngOnInit() { this.personagemServ.getPersonagens().subscribe(personagens => { this.personagens = personagens; console.log('lenght', this.personagens.length); }); } }
问题根源
问题其实出在Angular服务的单例特性上:
- 因为服务是
providedIn: 'root'的单例模式,构造函数只会在应用启动时执行一次 - 在构造函数中初始化的
personagensObservable,第一次被订阅后就进入了"冷态",不会再主动请求Firestore的新数据 - 当组件第二次进入
/list路由时,调用getPersonagens()拿到的是同一个旧Observable,订阅它不会触发新的快照请求,自然拿不到数据
解决后的代码
我调整了服务和组件的代码逻辑,核心是让每次组件请求数据时都拿到一个新的Observable,确保能重新触发Firestore的快照查询:
修改后的服务代码
// 移除全局的personagens Observable声明 personagemColl: AngularFirestoreCollection; personagemDoc: AngularFirestoreDocument; constructor(private afs: AngularFirestore) { this.personagemColl = this.afs.collection('personagem', ref=> ref.orderBy('nome', 'asc')); } getPersonagens$(): Observable { return this.personagemColl.snapshotChanges().map(changes=> { return changes.map(p => { const data = p.payload.doc.data() as Personagem; data.id = p.payload.doc.id; return data; }); }); } // add/delete/update等其他方法保持不变
修改后的组件代码
personagens: Personagem[]; constructor(private personagemServ: PersonagemService, private router: Router) { } ngOnInit() { this.personagemServ.getPersonagens$().subscribe( personagens => { this.personagens = personagens; }); }
这样修改后,每次组件进入/list路由执行ngOnInit时,都会调用getPersonagens$()获取新的Observable并订阅,就能重新拿到Firestore的快照数据,路由切换后列表也能正常显示了。
内容的提问来源于stack exchange,提问作者WSouBar
相关产品推荐
相关产品推荐

