Angular2中无需用户操作,获取数据后更新组件视图的方法
解决Angular 2中数据获取后自动更新视图的问题
我来帮你排查下问题根源,然后一步步解决它:
问题1:模板绑定错了对象
你的GameTableComponent里已经通过订阅scoresObserver把数据同步到了组件自己的gameRows变量,但模板里却直接调用gameTableService.getGameRows()来渲染。Angular的变更检测是针对组件属性的,所以模板应该绑定组件内部的gameRows,而不是直接依赖服务的方法。
问题2:Firebase回调在Angular变更检测之外
Firebase的on('value')回调是在Angular的NgZone上下文外执行的,这就导致你更新了数据,但Angular不知道要触发变更检测——这就是为什么你点击组件才会刷新,因为点击动作手动触发了变更检测。
问题3:数组引用未更新(可选优化)
你在processData里是往原数组push元素,然后把同一个数组引用通过Subject发送出去。虽然订阅能收到,但Angular对数组内容变化的检测有时候不如引用变化灵敏,建议每次更新时创建新数组,确保引用改变。
修改后的代码
1. 调整DataStorageService,把Firebase回调放到NgZone里
import { NgZone } from '@angular/core'; export class DataStorageService { constructor( private db: AngularFireDatabase, private gameTableService: GameTableService, private ngZone: NgZone // 注入NgZone ) { } getData() { this.db.database.ref('gameRows').on('value', snapshot => { // 用ngZone.run把回调包裹起来,确保在Angular上下文内执行 this.ngZone.run(() => { this.gameTableService.processData(snapshot.val()); console.log(snapshot.val()); }); }); } }
2. 更新GameTableService的processData方法,创建新数组
export class GameTableService { gameRows: GameRowModel[] = []; scoresObserver: Subject<GameRowModel[]> = new Subject<GameRowModel[]>(); constructor(private playerService: PlayerService) { } processData(gameRow: GameRowModel) { // 用扩展运算符创建新数组,替换原数组 this.gameRows = [...this.gameRows, this.addRow(gameRow)]; this.playerService.getPlayers().forEach((player, index) => { player.updatePlayerData(gameRow.playerScores[index]); }); this.scoresObserver.next(this.gameRows); } // 补充getGameRows方法(如果之前没实现的话) getGameRows(): GameRowModel[] { return this.gameRows; } }
3. 修改GameTableComponent的模板,绑定组件内部的gameRows
<div class="col border-warning border"> <div class="row"> <div class="col-md-1 text-center">#</div> <div class="col text-center" *ngFor="let player of playerService.getPlayers()"> {{player.name}} </div> </div> <div style="overflow-y: scroll; overflow-x: hidden; max-height: 250px;"> <!-- 这里改成绑定组件的gameRows变量 --> <app-game-row class="text-center" *ngFor="let row of gameRows" [gameRow]="row"></app-game-row> </div> </div>
这样修改后,当Firebase返回新数据时,回调会在Angular的上下文内执行,更新服务的数组并发送新引用,组件订阅后更新自己的gameRows,模板绑定这个变量后,Angular会自动触发变更检测,视图也就会实时更新了,不需要再手动点击组件。
内容的提问来源于stack exchange,提问作者Jarek Sularz
相关产品推荐
相关产品推荐

