How to use httpResource to fetch an image and render in HTML as a URL

How to use httpResource to fetch an image and render in HTML as a URL

I am experimenting with httpResource, I am trying to fetch an image using the new httpResource method, but I am not sure how to convert the fetched image render in an img tag.

Below is a sample of what I am trying to do, I checked the network tab and I can see the image is being fetched, but it is not showing inside the image tag.

Angular.dev - HttpResource Docs

I think I need to convert this to a URL.

@Component({
  selector: 'app-root',
  imports: [FormsModule, CommonModule],
  template: `
    <input [(ngModel)]="height" type="number"/>
    <input [(ngModel)]="width" type="number"/>
    <hr/>
    @let imageStatus = imageResource.status();
    @if(imageResource.value(); as imageVal) {
      <img [src]="imageVal"/>
    } 
  `,
})
export class App {
  sanitizer = inject(DomSanitizer);
  height = signal<number>(200);
  width = signal<number>(300);
  rs = ResourceStatus;
  imageResource = httpResource(
    () => `https://picsum.photos/${this.height()}/${this.width()}`
  );
}

Stackblitz Demo

Answer

If you want to fetch an image, then I think httpResource.blob will be the best option for fetching the image.

I am choosing blob because it is the minimal type (Read the data, but do not write) to achieve the functionality What is the difference between an ArrayBuffer and a Blob?

imageResource = httpResource.blob(() => `https://picsum.photos/${this.height()}/${this.width()}`, { ... });

But the HTML element img accepts either a path or a URL to the resource.

So we need to convert this blob to a url string.

We can then leverage parse property of the second argument of httpResource which is a part of httpResourceOptions, this can be used to transform the fetched blob into a URL.

sanitizer = inject(DomSanitizer);
height = signal<number>(200);
width = signal<number>(300);
rs = ResourceStatus;
imageResource = httpResource.blob(
  () => `https://picsum.photos/${this.height()}/${this.width()}`,
  {
    parse: (blob: Blob) => {
      let image = null;
      if (blob) {
        let objectURL = URL.createObjectURL(blob);
        image = this.sanitizer.bypassSecurityTrustUrl(objectURL);
        return objectURL;
      }
      return image;
    },
  }
);

We can then use URL: createObjectURL() to convert the blob to a URL string.4

As an additional safety, we can sanitize the url using the DomSanitizer method bypassSecurityTrustUrl and finally return the safe url.

We can use @switch along with the resource api signals:

status -> status of the resource fetched

value -> value of the resource fetched

Combine this with ResourceStatus, we can show loading status and error status.

<input [(ngModel)]="height" type="number"/>
<input [(ngModel)]="width" type="number"/>
<hr/>
@let imageStatus = imageResource.status();
@switch(imageStatus) {
  @case (rs.Resolved) {
    @let image = imageResource.value();
    <img [src]="image"/>
  } 
  @case (rs.Error) {
  Failed to fetch the image...
  } 
  @default {
    Loading...
  } 
}

Full Code:

import {
  Component,
  signal,
  computed,
  ResourceStatus,
  inject,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { DomSanitizer } from '@angular/platform-browser';
import {
  httpResource,
  HttpResourceOptions,
  HttpResourceRequest,
  provideHttpClient,
} from '@angular/common/http';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  imports: [FormsModule, CommonModule],
  template: `
    <input [(ngModel)]="height" type="number"/>
    <input [(ngModel)]="width" type="number"/>
    <hr/>
    @let imageStatus = imageResource.status();
    @switch(imageStatus) {
      @case (rs.Resolved) {
        @let image = imageResource.value();
        <img [src]="image"/>
      } 
      @case (rs.Error) {
      Failed to fetch the image...
      } 
      @default {
        Loading...
      } 
    }
  `,
})
export class App {
    sanitizer = inject(DomSanitizer);
    height = signal<number>(200);
    width = signal<number>(300);
    rs = ResourceStatus;
    imageResource = httpResource.blob(
      () => `https://picsum.photos/${this.height()}/${this.width()}`,
      {
        parse: (blob: Blob) => {
          let image = null;
          if (blob) {
            let objectURL = URL.createObjectURL(blob);
            image = this.sanitizer.bypassSecurityTrustUrl(objectURL);
            return objectURL;
          }
          return image;
        },
      }
    );
}

bootstrapApplication(App, {
  providers: [provideHttpClient()],
});

Stackblitz Demo

Enjoyed this question?

Check out more content on our blog or follow us on social media.

Browse more questions