Angular RxJS - How to monitor progress of HTTP Get Request (not file)

abdul-wahab picture abdul-wahab · Jan 26, 2018 · Viewed 9.3k times · Source

How to utilize Angular HTTPClient's progress event to show progress in percentage of Get request which does not necessarily a file request?

Currently HTTPClient's progress event fires after request completion. I am hoping to work with Content-Length at back end and determine percentage of content loaded at front end.

I am loading a large amount of rows for a grid and need to show incremental progress on UI. Is it possible?

Answer

Sebastian picture Sebastian · Feb 12, 2020

I know this question is older but, i stumbled upon this while searching for an answer to a similar problem and since there is no accepted answer i post my solution.

I recently implemented a generic way to display a progress bar for every request no matter the type in angular 8.

First i created a HttpInterceptor which would automatically intercept every http call where the reportProgress option is set to true.

@Injectable()
export class HttpProgressInterceptor implements HttpInterceptor {

  constructor(
    private spinnerService: SpinnerService // my personal service for the progress bar - replace with your own
  ) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.reportProgress) {
      // only intercept when the request is configured to report its progress
      return next.handle(req).pipe(
        tap((event: HttpEvent<any>) => {
          if (event.type === HttpEventType.DownloadProgress) {
            // here we get the updated progress values, call your service or what ever here
            this.spinnerService.updateGlobalProgress(Math.round(event.loaded / event.total * 100)); // display & update progress bar
          } else if (event.type === HttpEventType.Response) {
            this.spinnerService.updateGlobalProgress(null); // hide progress bar
          }
        }, error => {
          this.spinnerService.updateGlobalProgress(null); // hide progress bar
        })
      );
    } else {
      return next.handle(req);
    }
  }
}

You need to register this interceptor in your module of course:

@NgModule({
  declarations: [
    AppComponent,
    ...
  ],
  imports: [
    BrowserModule,
    ...
    RouterModule.forRoot(appRoutes)
  ],
  providers: [
    ...
    { provide: HTTP_INTERCEPTORS, useClass: HttpProgressInterceptor, multi: true },
    ...}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Basically we're done here, only thing left is that we need to change the way we call our apis. If you want a specific request to be monitored using this interceptor you need to tell angular to report the progress on the HttpRequest:

@Injectable()
export class MyService {

  constructor(
    private http: HttpClient
  ) {}

  myGetMethod() {
    const url = "api/data/load/big/data";
    const req = new HttpRequest("GET", url, {
      reportProgress: true  // this is important!
    });

    return this.http.request(req);
  }
}

This way of calling the httpClient api deliveres a different object when calling .subscribe so we need to take care of that when calling the myGetMethod():

ngOnInit() {
  this.myService.myGetMethod().subscribe((event: HttpEvent<any>) => {
    if (event.type === HttpEventType.Response) {
      const responseData = event.body;
      console.dir(responseData); // do something with the response
    }
  });
}

We could also listen here for the HttpEventType.DownloadProgress event and update the progress values inside this component - but that was not the point of my example.

Hint: if you encounter the problem that event.total is undefined - you must check whether your REST backend REALLY is providing the Content-Length header - if this header is missing, you will not be able to calculate the progress!

anyway, i hope this will help somebody someday 😉