in my web app user can create objects in one form (one of it's field is image) and make sets of them (by selecting number of them from list) in another form. There is third component that displays list of all sets. Since every non-set object has it's own image and can be part of more than one set i stumbled upon one problem. In my list of sets I don't want to load the same image more than once. I figured out something like this:
export class SetListComponent implements OnInit {
private sets: ObjectSet[];
images: Map<number, ReplaySubject<string>>;
constructor(){
this.image = new Map();
this.sets = [];
}
ngOnInit() {
this.reloadSets();
}
reloadSets() {
this.prizeService.getAllSets().subscribe(prizeSets => {
this.updateImageMap(prizeSets);
this.prizeSets = prizeSets;
})
}
private updateImageMap(sets: ObjectSet[]): void {
sets.map(set => set.elementsList)
.reduce((acc, x) => acc.concat(x), [])
.map(prize => prize.id)
.forEach(objectId => {
if (!this.images.has(objectId)) {
this.images.set(objectId, new ReplaySubject(1));
this.getImageFromAPI(objectId);
}
});
}
private getImageFromAPI(objectId: number) {
const reader = new FileReader();
reader.onload = (e) => {
this.images.get(objectId).next(e.target['result']);
};
this.objectService.getImage(objectId)
.subscribe(blob => {
reader.readAsDataURL(blob);
});
}
getObjectImage(objectId: number): Subject<string> {
console.log('getObjectImage: '+objectId);
return this.images.get(objectId);
}
}
Basically in ngOnInit
reloads sets, that is for objectId
adds <objectId, new ReplaySubject(1)>
entry to map and then starts to load image from API. There is small HTML sample:
<mat-expansion-panel *ngFor="let set of objectSets" class="m-2">
<ng-container *ngFor="let obj of set.elementsList">
<mat-list-item>
<h4 mat-line>{{obj.name}}</h4>
<p><img [src]="getObjectImage(obj.id) | async" alt="{{obj.id}}"/></p>
</mat-list-item>
</ng-container>
</mat-expansion-panel>
I decided to use ReplaySubject because I read that it can be used by more than one subscriber and it always returns last values when new subscriber comes (in this case only one last value). I thought that my HTML will invoke method getObjectImage
and will subscribe to ReplaySubject
. Because of console.log('getObjectImage: '+objectId);
I found out that this method is invoked way more times than there are objects in my list.
My questions:
- Is it ok that method is invoked more than once per object?
- Is ReplaySubject the good thing to use in that case?