Angular中使用RxJS获取FireStorage URL实现渲染前加载图片问题咨询
问题解决方法
问题根因
你遇到的是异步操作时序和RxJS操作符使用错误的问题:
- 从Firebase Storage获取下载链接的
getDownloadURL()是异步Promise操作,ngOnInit不会等待这些异步请求完成就会结束,所以初始渲染时arrayBouquetSrc自然是空数组 - 你当前用
map操作符直接返回Promise,之后订阅的时候嵌套then的写法会导致Angular变更检测不能及时触发,同时你拿到的url是纯字符串,但是模板里还要取boucket.name字段,你直接把url push进数组的写法本身就存在逻辑错误,无法匹配模板的取值逻辑
修复方案
第一步:调整Typescript逻辑
修正RxJS异步操作写法,用forkJoin等待所有URL都获取完成后再整体赋值数组,同时保留name字段和url的对应关系:
export class MyBouquetComponent implements OnInit { email : any Data : any userInfo : any; arrayBouquetName : string[] = [] // 调整数组类型,存储包含名称和链接的对象 arrayBouquetSrc : {name: string, url: string}[] = [] // 增加加载状态控制渲染逻辑 isLoading = true constructor( private fireStore : AngularFirestore, private loginServise : IsLoginService, private storage: AngularFireStorage, public _DomSanitizationService: DomSanitizer, // 注入NgZone确保异步操作触发变更检测 private ngZone: NgZone ) {} async ngOnInit() { this.email = await this.loginServise.userInfo(); this.arrayBouquetName = (await this.fireStore.collection("user").doc(this.email).get().toPromise()).get("src") // 生成所有下载链接的请求队列 const urlRequests = this.arrayBouquetName.map(item => { return from(firebase.storage().ref(`${this.email}`).child(`${item}`).getDownloadURL()).pipe( // 把名称和链接拼接为对象返回 map(url => ({name: item, url: url})) ) }) // 等待所有请求完成后统一赋值数组 forkJoin(urlRequests).subscribe(res => { this.ngZone.run(() => { this.arrayBouquetSrc = res this.isLoading = false }) }) } }
第二步:调整模板代码
增加加载状态判断,同时修正图片链接的取值逻辑:
<div id="canvas-my-bouquet"> <div id="container-my-bouquet"> <!-- 加载状态下显示占位提示 --> <div *ngIf="isLoading">图片加载中...</div> <cdk-virtual-scroll-viewport *ngIf="!isLoading" appendOnly itemSize="50" class="my-bouquet-scroll"> <div *cdkVirtualFor="let boucket of arrayBouquetSrc" class="bouquet-item"> <div id="drag-items"> <span>{{boucket.name}}</span> <img [src]="_DomSanitizationService.bypassSecurityTrustUrl(boucket.url)" class="flower-image" [matTooltip]="" matTooltipPosition="right"> </div> </div> </cdk-virtual-scroll-viewport> </div> </div>
可选进阶方案:路由Resolver预加载
如果需要完全在页面渲染前就拿到所有数据,可以配置Angular路由的Resolver守卫,在路由激活前就完成所有Firebase请求,进入页面时数组已经填充完成,不需要额外处理加载状态。
内容的提问来源于stack exchange,提问作者Boris Park
相关产品推荐
相关产品推荐

