Angular项目Array<Array<string>>数据需点击输入框才渲染如何修复
问题根因
- 你存在Promise与RxJS嵌套订阅混用的逻辑错误:
Promise.all(promises).then执行时,内部嵌套的snap.subscribe、getLatLng.subscribe等异步回调尚未执行,此时赋值给this.matchingDocs的是空数组。你在控制台看到的有值是浏览器控制台的引用特性:打印对象时输出的是引用,展开查看时才会读取当前最新值,实际代码执行console.log的瞬间matchingDocs是空的。 - 后续异步回调中对局部
matchingDocs执行push操作时,你没有同步更新组件的this.matchingDocs,也没有触发Angular变更检测,导致视图无法响应数据更新。 - ngOnInit中调用
detectChanges无效,因为此时异步查询还未返回结果。
修复方案
方案1:快速修复现有逻辑
- 去掉局部的
matchingDocs变量,直接操作组件的this.matchingDocs - 每次
push后触发变更检测 - 修正后的
findArround代码示例:
findArround(lat: number, long: number, ray: number) { const geofire = require('geofire-common'); const center = [lat, long]; const radiusInM = ray * 20; const bounds = geofire.geohashQueryBounds(center, radiusInM); const promises = []; for (const b of bounds) { const q = this.db.collection('feeder') promises.push(q.get()); } this.matchingDocs = []; // 先清空旧数据 const alreadyMatched: Array<string> = []; Promise.all(promises).then((snapshots) => { for (const snap of snapshots) { snap.subscribe(res => { res.forEach(data => { if(alreadyMatched.includes(data.id)) return; this.feederService.getLatLng(data.id).pipe( switchMap(r => { const lat = r[0]; const lng = r[1]; const distanceInKm = geofire.distanceBetween([lat, lng], center); const distanceInM = distanceInKm * 1000; if (distanceInM > radiusInM) return []; alreadyMatched.push(data.id); return this.authService.getUser(); }), switchMap(user => { const uid = this.authService.isAuth() ? user.uid : 'noUid'; return this.feederService.getFeederById(data.id).pipe( map(feeder => feeder.state ? [data.id, uid] : null) ) }) ).subscribe(result => { if(result) { this.matchingDocs.push(result); this.changeDetection.markForCheck(); // 每次更新后触发变更检测 } }) }) }) } }); }
方案2:规范实现(推荐)
完全使用RxJS操作符整合所有异步流,避免嵌套订阅和Promise混用,等所有数据加载完成后一次性更新数据并触发变更检测,性能更好也更易维护。
import { from, forkJoin } from 'rxjs'; import { map, switchMap, filter } from 'rxjs/operators'; // 省略其他代码 findArround(lat: number, long: number, ray: number) { const geofire = require('geofire-common'); const center = [lat, long]; const radiusInM = ray * 20; const bounds = geofire.geohashQueryBounds(center, radiusInM); const alreadyMatched = new Set<string>(); // 把所有查询转成Observable流 const queryStreams = bounds.map(b => from(this.db.collection('feeder').get())); forkJoin(queryStreams).pipe( // 展开所有查询结果 map(snapshots => snapshots.flatMap(snap => snap.docs)), // 逐个处理每个文档 switchMap(docs => { const docStreams = docs.map(data => { if(alreadyMatched.has(data.id)) return []; return this.feederService.getLatLng(data.id).pipe( filter(r => { const distanceInM = geofire.distanceBetween([r[0], r[1]], center) * 1000; return distanceInM <= radiusInM; }), switchMap(() => { alreadyMatched.add(data.id); return this.authService.getUser(); }), switchMap(user => { const uid = this.authService.isAuth() ? user.uid : 'noUid'; return this.feederService.getFeederById(data.id).pipe( map(feeder => feeder.state ? [data.id, uid] : null) ) }), filter(res => !!res) ) }) return forkJoin(docStreams); }) ).subscribe(results => { this.matchingDocs = results.filter(r => !!r); this.changeDetection.detectChanges(); // 一次性触发变更检测 }) }
内容的提问来源于stack exchange,提问作者Franck Gomez
相关产品推荐
相关产品推荐

