Angular 2 HTTP Progress bar Angular 2 HTTP Progress bar angular angular

Angular 2 HTTP Progress bar


Currently (from v. 4.3.0, when using new HttpClient from @ngular/common/http) Angular provides listening to progress out of the box. You just need to create HTTPRequest object as below:

import { HttpRequest } from '@angular/common/http';...const req = new HttpRequest('POST', '/upload/file', file, {  reportProgress: true,});

And when you subscribe to to request you will get subscription called on every progress event:

http.request(req).subscribe(event => {  // Via this API, you get access to the raw event stream.  // Look for upload progress events.  if (event.type === HttpEventType.UploadProgress) {    // This is an upload progress event. Compute and show the % done:    const percentDone = Math.round(100 * event.loaded / event.total);    console.log(`File is ${percentDone}% uploaded.`);  } else if (event instanceof HttpResponse) {    console.log('File is completely uploaded!');  }});

More info here.


You could leverage the onprogress event provided by XHR (see this plunkr: http://plnkr.co/edit/8MDO2GsCGiOJd2y2XbQk?p=preview).

This allows to get hints about the progress of the download. This isn't supported out of the box by Angular2 but you can plug it by extended the BrowserXhr class:

@Injectable()export class CustomBrowserXhr extends BrowserXhr {  constructor(private service:ProgressService) {}  build(): any {    let xhr = super.build();    xhr.onprogress = (event) => {      service.progressEventObservable.next(event);    };    return <any>(xhr);  }}

and override the BrowserXhr provider with the extended:

bootstrap(AppComponent, [  HTTP_PROVIDERS,  provide(BrowserXhr, { useClass: CustomBrowserXhr })]);

See this question for more details:


When you make http cals in angular2, it returns an Observable of type Response, this response is created inside class called XHRConnection where all the magic happens.

The XHRConnection builds the response by listening to XMLHttpRequest's load event, this means it will return one response at the end of the request.

Now to be able to alter this behavior we need to make our connection class listen to the progress event.

So we need to create custom Connection class, to handle the response as we see fit.

I did it this way,Take note that my php API returns multi response in a single request and this responses are plain strings.

my_backend.ts

import {Injectable} from "angular2/core";import {Observable} from "rxjs/Observable";import {Observer} from "rxjs/Observer";import {Connection,ConnectionBackend} from "angular2/src/http/interfaces";import {ReadyState, RequestMethod, ResponseType} from "angular2/src/http/enums";import {ResponseOptions} from "angular2/src/http/base_response_options";import {Request} from "angular2/src/http/static_request";import {Response} from "angular2/src/http/static_response";import {BrowserXhr} from "angular2/src/http/backends/browser_xhr";import {Headers} from 'angular2/src/http/headers';import {isPresent} from 'angular2/src/facade/lang';import {getResponseURL, isSuccess} from "angular2/src/http/http_utils"export class MyConnection implements Connection {     readyState: ReadyState;    request: Request;    response: Observable<Response>;    constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {               this.request = req;        this.response = new Observable<Response>((responseObserver: Observer<Response>) => {            let _xhr: XMLHttpRequest = browserXHR.build();            _xhr.open(RequestMethod[req.method].toUpperCase(), req.url);            // save the responses in array             var buffer :string[] = [];             // load event handler            let onLoad = () => {                let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;                //_xhr.respons 1 = "Loading data!"                //_xhr.respons 2 = "Loading data!Ready To Receive Orders."                // we need to fix this proble                 // check if the current response text contains the previous then subtract                // NOTE: I think there is better approach to solve this problem.                buffer.push(body);                if(buffer.length>1){                    body = buffer[buffer.length-1].replace(buffer[buffer.length-2],'');                }                let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());                let url = getResponseURL(_xhr);                let status: number = _xhr.status === 1223 ? 204 : _xhr.status;                let state:number = _xhr.readyState;                if (status === 0) {                    status = body ? 200 : 0;                }                var responseOptions = new ResponseOptions({ body, status, headers, url });                if (isPresent(baseResponseOptions)) {                    responseOptions = baseResponseOptions.merge(responseOptions);                }                let response = new Response(responseOptions);                //check for the state if not 4 then don't complete the observer                if(state !== 4){                    //this will return stream of responses                    responseObserver.next(response);                    return;                }                else{                    responseObserver.complete();                    return;                }                responseObserver.error(response);            };            // error event handler            let onError = (err: any) => {                var responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error });                if (isPresent(baseResponseOptions)) {                    responseOptions = baseResponseOptions.merge(responseOptions);                }                responseObserver.error(new Response(responseOptions));            };            if (isPresent(req.headers)) {                req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));            }            _xhr.addEventListener('progress', onLoad);            _xhr.addEventListener('load', onLoad);            _xhr.addEventListener('error', onError);            _xhr.send(this.request.text());            return () => {                _xhr.removeEventListener('progress', onLoad);                _xhr.removeEventListener('load', onLoad);                _xhr.removeEventListener('error', onError);                _xhr.abort();            };        });    }}@Injectable()export class MyBackend implements ConnectionBackend {  constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}  createConnection(request: Request):MyConnection {    return new MyConnection(request, this._browserXHR, this._baseResponseOptions);  }}

And in the app.component.ts

import {Component, provide} from 'angular2/core';import {HTTP_PROVIDERS,XHRBackend} from 'angular2/http';import {MyBackend} from './my_backend';@Component({    selector: 'my-app',    providers:  [        HTTP_PROVIDERS,        MyBackend,        provide(XHRBackend, {useExisting:MyBackend})    ]    .    .    .

Now calling http.get will return a steam of responses.