Reputation: 161
Let’s say I have a list of cars:
const cars: Array<{carId: number}> = [{carId: 1}, {carId: 2}];
And I render them in a template:
<div *ngFor=“let car of cars”></div>
Inside of that template, I want to get some dynamic image paths from a method that returns an observable:
<div *ngFor=“let car of cars”>
<img [src]=“getImagePath(car) | async”>
</div>
getImagePath(car: {carId: number}): Observable<string> {
return //some service request that brings back a url based on the carId
}
For state management and storage, I’m using Firebase. All of my images are stored in a bucket and I can get to them by providing my service with a carId that uses it to fetch the downloadURL.
I want to avoid updating Firestore with downloadURL’s when the file is first saved because I’d be storing them in sub-collections and pulling them out of there is already a pain due to the nature of Firestore.
Is there a way to do this more efficiently? The code above would most certainly bring the browser to a crawl if the list starts to grow … right?
Upvotes: 0
Views: 527
Reputation: 796
Instead of calling a request directly on every img element, you can just make it into an observable and subscribe to it once then display the returned array to your html.
I used a couple of RXJS operators to get what you want.
mergeMap
to flatten the inner observableof
to convert the cars array into an observablemap
to transform the cars array into a a new array with their corresponding car image pathforkJoin
- we wrapped multiple requests into one observable and will only return when a response has been received for all requests.This is certainly only one way to do it and there might be other better ways.
getCarImages() {
const cars: Array < {
carId: number;
} > = [{
carId: 1,
},
{
carId: 2,
},
];
of(cars)
.pipe(
mergeMap((e) => {
return forkJoin(
e.map((item) => this.apiService.getCarImages(item.carId))
).pipe(
map((paths) => {
console.log(paths);
return e.map((car, index) => ({
...car,
imagePath: paths[index],
}));
})
);
})
)
.subscribe((res) => {
this.cars = res;
console.log(this.cars);
});
}
HTML:
<div *ngFor="let car of cars">
<img [src]="car?.imagePath" />
</div>
I created a stackblitz to mock the solution.
Upvotes: 0