How to get the data of an img loaded from a src url locally?
I have a list of images which have all src retrieved from backend API:
<div class="repoContent"> <div *ngFor="let item of repo.value" class="repoItem"> <img [src]="fetchImage(item.name)" (click)="selectAsset(fetchImage(item.name))" alt=""> </div> </div>
After users can see the images they can click on them to put one to ngx-image-cropper for editting. Here I am using urls again as inputs. (could be blobs base64)
The thing is:
For each time I clicked on an image, another http request was fired to my backend for the image while I assume those were cached locally. As I can see those images, I think the data is somewhere stored at client machine that I can get then pass the blob data to ngx-image-cropper without using urls in order to avoid requesting backend again. How could I do that?
update for functions:
selectAsset(url){ this.imageSelected=true; this.imageFile=null; this.imageURL=url; } fetchImage(name:String):String{ return GlobalParameterService.apiAssetGet+"?name="+name; }
selectAsset resets cropper and put url in.
fetchImage builds target url
You’re triggering the request twice explicitly with the function fetchImage(item.name)
. And it’s generally a bad design to bind a function to a property. If you aren’t handling the change detection, the functions will be triggered multiple times.
What you could instead do is to fetch the images in the controller and use them in the template. Try the following
Controller
import { from, forkJoin } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; export class AppComponent { imageBase64 = ''; croppedImage: any = ''; repo = { value: [ { name: '100' }, { name: '200' }, { name: '400' }, { name: '500' } ] }; constructor() { } ngOnInit() { const urls = this.repo.value.map(item => this.fetchImage(item.name)); forkJoin(urls).pipe( switchMap(responses => { return forkJoin(responses.map(response => this.getBase64(response))); }) ).subscribe( (bases: Array<string>) => { bases.forEach((base, i) => this.repo.value[i]['base'] = base); }, err => { console.log(err); } ); } fetchImage(name) { return from(fetch(`https://picsum.photos/id/${name}/200/200`)); } // Credit: https://stackoverflow.com/a/20285053/6513921 getBase64(response) { return from(response.blob().then(blob => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }))); } selectAsset(base: string) { this.imageBase64 = base; } imageCropped(image: any) { this.croppedImage = image.base64; } imageLoaded() { // show cropper } loadImageFailed() { // show message } }
Template
<div class="repoContent"> <div *ngFor="let item of repo.value" class="repoItem"> <img style="cursor: pointer" [src]="item.base" #img (click)="selectAsset(item.base)" alt=""> </div> </div> <hr> <image-cropper [imageBase64]="imageBase64" [resizeToWidth]="0" format='png' (imageCropped)="imageCropped($event)" (imageLoaded)="imageLoaded()" (loadImageFailed)="loadImageFailed()" ></image-cropper> <img [src]="croppedImage" />
Here I’ve used the repo
variable and https://picsum.photos
API as examples. Replace it with your own variables. I’ve also used RxJS from
function to convert Promises to Observables and forkJoin
function to combine multiple observables.
Working example: Stackblitz