Joe Keene
Joe Keene

Reputation: 2351

Angular 2.0 How to Extend BrowserXhr to create progress bar?

so I'm trying to make an accurate progress bar that fills up based on the my ajax call.

This helped but I think that perhaps things have changed a little since it was written.

So I have my CustomBrowserXhr that I've added to my App Module providers to ovverride the BrowserXhr:-

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {

constructor(private service: ProgressService) {}


    build(): any {
        let xhr = super.build();
        xhr.onprogress = (event) => {
            console.log(event, "event inside browser override");
            this.service.progressEventObservable.next(event);
        };
        return <any>(xhr);
    }
}

I then have a very basic progress service which other components can subscribe to:-

@Injectable()
export class ProgressService {
    progressEventObservable:Subject<any> = new Subject();
    progressEvent$ = this.progressEventObservable.asObservable();
}

I thought that I could just make http calls and I would see the console log "event inside browser override" - as it is I just get the error "EXCEPTION: browserXHR.build is not a function". Can someone shed some light one what's going wrong here please?

Thanks in advance.

Upvotes: 2

Views: 2535

Answers (1)

wolfhoundjesse
wolfhoundjesse

Reputation: 1133

Edit: I added the call to super(); and attached a few useful things to the event object.

Thanks for your help. This works for me. I threw both of these services into one file for brevity:

import { Injectable } from '@angular/core';
import { BrowserXhr } from '@angular/http';
import { Subject } from 'rxjs/Rx';

@Injectable()
export class ProgressService {
    progressEventObservable: Subject<any> = new Subject();
    progressEvent$ = this.progressEventObservable.asObservable();
}


@Injectable()
export class CustomBrowserXhr extends BrowserXhr {

constructor(private service: ProgressService) { super(); }

    build(): any {
        let xhr = super.build();
        let startTime = Date.now();
        xhr.upload.onprogress = (event) => {
            let timeElapsed = Date.now() - startTime;
            let uploadSpeed = event.loaded / (timeElapsed / 1000);
            let timeRemaining = Math.ceil(((event.total - event.loaded) / uploadSpeed));

            event.uploadTimeRemaining = timeRemaining;
            event.uploadSpeed = (uploadSpeed / 1024 / 1024).toFixed(2);
            event.percentCompleted = ((event.loaded / event.total) * 100).toFixed(0);
            console.log(event, "event inside browser override");
            this.service.progressEventObservable.next(event);
        };
        return <any>(xhr);
    }
}

I added the two services to my main module, just as you described:

providers: [
    ProgressService,
    { provide: BrowserXhr, useClass: CustomBrowserXhr }
],
bootstrap: [ AppComponent ]

It logged to the console as expected. Then, in the component where I upload files, I had to force change detection, but it otherwise works fine:

constructor(
private cdr: ChangeDetectorRef,
private service: ProgressService
) { 
    this.service.progressEvent$.subscribe(event => {
        this.progress = event.percentCompleted;
        this.speed = event.uploadSpeed;
        this.timeRemaining = event.uploadTimeRemaining;
        this.cdr.detectChanges();
    });
}

I tried to get it to work with async pipe, but it was only updating on the last event. What do you think?

Upvotes: 2

Related Questions