I am using Angular 12 with AngularFire / Firebase 9 (Modular). I am struggling with using RxJS to retrieve the downloadURL from storage and adding it to each item of a collection result. Using the following code, I can view the data in the console under "ZoneAwarePromise" but I can not see the data through the template.
const listCollection = collectionData(collection(this.firestore, 'lists'), {idField: 'id'});
this.lists = listCollection.pipe(take(1), map(lists => lists.map(async list => {
const imgRef = ref(this.storage, list.metaImage);
const coverImg = await getDownloadURL(imgRef);
return {...list, coverImg}
})));
It's hard to tell for sure from what you've written here but it looks like this.lists is an observable that emits a list of promises. That's what the async keyword does, it's syntactic sugar for creating promises.
You haven't taken/used the values from the promises anywhere (Ie: You're not awaiting them anywhere).
One solution is to rewrite your promises as observables use forkJoin to run your array of observables.
That might look like this:
const listCollection = collectionData(collection(this.firestore, 'lists'), {idField: 'id'});
this.lists = listCollection.pipe(
take(1),
map(lists => lists.map(list => {
const imgRef = ref(this.storage, list.metaImage);
return from(getDownloadURL(imgRef)).pipe(
map(coverImg => ({...list, coverImg}))
)
})),
switchMap(listsCalls => forkJoin(listsCalls))
);
Using a switchMap to combine the results does work; however it causes some render blocking issues in Angular (i.e. Waiting for all "list" items to return getDownloadUrl() before the template can display results). Removing "async" and "await" from the original code and adding an "async" pipe to the Angular template allows data to flow without render blocking.
list.component.ts
const listCollection = collectionData(collection(this.firestore, 'lists'), {idField: 'id'});
this.lists = listCollection.pipe(take(1), map(lists => lists.map(list => {
const imgRef = ref(this.storage, list.metaImage);
const coverImg = getDownloadURL(imgRef);
return {...list, coverImg}
})));
list.component.html
<div class="card" *ngFor="let list of lists | async">
<div class="card-image">
<img [src]="list.coverImg | async" />
</div>
<h2 class="card-title-lower">
<a [routerLink]="['/list', list.id]">{{ list.title }}</a>
</h2>
</div>