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

14,571

Solution 1

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 😉

Solution 2

What about this:

import { HttpEventType, HttpClient, HttpRequest} from '@angular/common/http';

...

const request = new HttpRequest('GET', url,  {
  reportProgress: true
});

http.request(request).subscribe(event => {

  // progress
  if (event.type === HttpEventType.DownloadProgress) {
    console.log(event.loaded, event.total); 
    // event.loaded = bytes transfered 
    // event.total = "Content-Length", set by the server

    const percentage = 100 / event.total * event.loaded;
    console.log(percentage);
  }

  // finished
  if (event.type === HttpEventType.Response) {
    console.log(event.body);
  }

})
Share:
14,571
abdul-wahab
Author by

abdul-wahab

I am a Front End Web Developer with a good grasp on back end technologies. I like the evolution of JavaScript into ES and TypeScript and advancements of Angular and React. I am always looking to improve JavaScript applications structure and build processes, hence npm helps me a lot. I am also keenly looking forward the time when we'll be able to develop similar or better native mobile and mobile web applications using cross platform technologies as we are able today to build using SDKs.

Updated on June 20, 2022

Comments

  • abdul-wahab
    abdul-wahab almost 2 years

    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?

  • abdul-wahab
    abdul-wahab over 6 years
    The three console.log prints just once, after the request has completed. loaded, total: 4 undefined progress: NaN response: null
  • Markus Kollers
    Markus Kollers over 6 years
    What angular version are you using?
  • abdul-wahab
    abdul-wahab over 6 years
    I am using 4.4.3.
  • Markus Kollers
    Markus Kollers over 6 years
    I don't understand your printed values? Is total empty?
  • abdul-wahab
    abdul-wahab over 6 years
    Yes, total is always undefined. Don't you think total shows Content-Length?
  • Markus Kollers
    Markus Kollers over 6 years
    Yes, the total value depends on the Content-Length-Header. Are you sure it is set in the response? And how large is your response? Only 4 bytes?
  • abdul-wahab
    abdul-wahab over 6 years
    I am setting it with response.Headers.Add("Content-Length", System.Text.ASCIIEncoding.ASCII.GetByteCount(serializedDataT‌​able).ToString());. It's a Nancy endpoint. Response is around 10KB. But this header doesn't show in Network. I am using webpack proxy for API requests. Any ideas?
  • Markus Kollers
    Markus Kollers over 6 years
    If the header isn't visible in network in your browser, your server isn't sending it. Different problem, different question ;-)
  • abdul-wahab
    abdul-wahab over 6 years
    Yeah, problem is, any header except Content-Length is sent. For some reason, content-length isn't getting through.
  • Natalie Perret
    Natalie Perret over 5 years
    It's written "not file"
  • Kamil Kiełczewski
    Kamil Kiełczewski about 4 years
    I also add near reportProgress: true following option: observe: "events" - this makes that progress bar refresh more often