How to get the time remaining for an api response to get resolved in angular?

Viewed 477

As you can see below is a network tab that shows the time taken & the loading indicator (on right - green).

Idea is to create a loading indicator that matches something similar (like a real progress bar, & not just some loading animation)

How can we achieve something in angular using rxjs?

enter image description here

Or atleast get this progress bar values from the network tab in to the angular app. Which then later can be cast in to something meaningful (progress bar/graph)

1 Answers

Angular uses two HTTP events to handle this: https://angular.io/api/common/http/HttpEventType

UploadProgress and DownloadProgress. You also need to include reportProgress: true as a parameter on the optional section of your http method.

(Note: for multiple upload/downloads at the same time you would want to use an array of subjects and forkjoin them together at the end. Alexander can explain it better here: https://medium.com/angular-in-depth/rxjs-recipes-forkjoin-with-the-progress-of-completion-for-bulk-network-requests-in-angular-5d585a77cce1

public upload(file: File): Observable<any> {
const result = new Subject<any>();   //the subject that will track progress
const fileUploadResult = {
  percentComplete: 0,  // the property you will have emitted to show progress.
  error: false  // you can also add on error, result, or any other variable needed
};
result.next(fileUploadResult);

const formData: FormData = new FormData();
formData.append('file', file, file.name);

const req = new HttpRequest('POST', this.endpointURL, formData, {
  reportProgress: true, // IMPORTANT: flag to tell it to report the progress
  responseType: 'text'
});

 this.httpClient.request(req).subscribe(
  event => {
    if (event.type === HttpEventType.UploadProgress) {  // if progress, get the % and emit it.
      const percentComplete = Math.round(100 * event.loaded / event.total) - 1;
      result.next({
        percentComplete,
        error: false
      });
    } else if (event instanceof HttpResponse) {
      result.next({
        percentComplete: 100,
        error: false,
        result: event.body
      });
      result.complete();
    }
  }, error => this.handleError(error, result));

return result;
}

So anyways, what you do is return the subject, or you can be safe an return an observable return result as Observable<any>;

handleError in my case is something like this, but it needs to get the subject to be able to emit to it:

  private handleError(error: HttpErrorResponse, result: Subject<FileUploadResult>) {
    const x: any = error;
    try {
      const errorResponse = JSON.parse(JSON.parse(x).message);
      result.next({
        percentComplete: 100,
        error: true,
        errorMessage: errorResponse.message,
        errors: errorResponse.errors
      });
    } catch {
      result.next({
        percentComplete: 100,
        error: true
      });
    }
    result.complete();

The actual subscription to such a method looks like this (I have my example doing a bit more special case handling, but hopefully the idea comes across):

 this.uploadService.upload(this.file).subscribe(
      r => {
        this.progress = r;
        this.showDelete = false;
        if (r.percentComplete === 100) {
          this.uploadComplete = true;
          if (r.error) {
            this.bar.nativeElement.classList.add('file-upload__upload-failure');
            this.progress = null;
            title = 'Upload Failed';
            if (this.basicError) {
              message = 'The upload failed.  Please try again later.';
            } else {
              if (r.errors) {
                errorList = r.errors;
              }
              if (r.errorMessage) {
                message = r.errorMessage;
              } else {
                message = 'The upload failed.  Please try again later.';
              }
            }
          } else {
            this.successBanner = true;

Hope this helps some people, Happy Coding!++

Related