3

For the note, I'm quite uninitiated to Angular (1 or 2 for that matter).

I'm trying to write a "super" layer of Http to avoid having to put the same headers everywhere.

import {Http, ConnectionBackend, RequestOptions, Response, Headers} from '@angular/http';
import {Observable} from 'rxjs';
import {LoadingService} from "../../services/loading.service";

export class HttpLoading extends Http {
    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,
        private _ls: LoadingService )
    {
        super(backend, defaultOptions);
    }

    getPostPutHeader() {
        var authHeader = new Headers();
        authHeader.append("Authorization", "Bearer "+ localStorage.getItem('token') );
        authHeader.append('Content-Type', 'application/json');
        return authHeader;
    }

    post(url: string, data:any):Observable<Response> {
        this._ls.isLoading = true; // Exception here: this._ls is undefined
        return super.post(url, data, { headers: this.getPostPutHeader() })
            .map(res => {
                this._ls.isLoading = false;
                return res;
            });
    }
}

And a service to tell when a request is executing; it's injected in the above class HttpLoading.

import {Injectable} from '@angular/core';

@Injectable()
export class LoadingService {
    isLoading: boolean = false;
}

I have a bunch of stuff in my bootstrap, including HttpLoading, LoadingService and ConnectionBackend (for this last one, I get an exception if it's not here).

bootstrap(AppComponent, [
    ConnectionBackend,
    HttpLoading,
    APP_ROUTER_PROVIDERS,
    HTTP_PROVIDERS,
    LoadingService,
    disableDeprecatedForms(),
    provideForms()
])

The problem is that the first time I call HttpLoading's post method (in yet another service), I get an exception at this._ls.isLoading, because this._ls is undefined, and I can't figure why.

Please tell me if you need more information.


Edit

LoadingService is correctly injected in my AppComponent (main component).

//imports
//@Component
export class AppComponent {
    requesting:boolean = false;

    constructor(public authService: AuthService, private router: Router, private _ls: LoadingService) {
    }

    navigate(route:string) {
        this._ls.isLoading = true;
        this.router.navigate([route])
            .then(() => this._ls.isLoading = false);
    }
}

Potential solution

It seems that your public/private parameters must be placed first in the list. I'll let someone more skilled than me explain why, though...

export class HttpLoading extends Http {

    constructor(private _ls: LoadingService, backend: ConnectionBackend, defaultOptions: RequestOptions) {
        super(backend, defaultOptions);
}
Kilazur
  • 3,089
  • 1
  • 22
  • 48

3 Answers3

2

I would configure your HttpLoading class this way in the providers when bootstrapping your application:

bootstrap(AppComponent, [
  (...)
  HTTP_PROVIDERS,
  {
    provide:Http,
    useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, loadingService: LoadingService) => {
      return new HttpLoading(backend, defaultOptions, loadingService);
    },
    deps: [XHRBackend, RequestOptions, LoadingService]
  }
]);

The reason for this is that you want to use your own class for the Http provider. You need to change the class behind the Http provider by your HttpLoading class. Be careful to define it after HTTP_PROVIDERS.

To be able to inject the instance of XHRBackend to your class, you need to use useFactory...

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • Could you please explain why, if that's not too much of a hassle? Also I think you're missing a closing curly bracket; I have an error in my IDE – Kilazur Jul 06 '16 at 20:10
  • I added some explanations in my answer... Feel free to tell me if it answers your question... Otherwise, you're right. A `{` needed to be removed ;-) Thanks for pointing this out! – Thierry Templier Jul 06 '16 at 21:35
  • Thanks a lot. I still don't understand everything, but that's a start! Does that "replace" automatically Http with HttpLoading when I use Http in my components? And should I replace ConnectionBackend with XHRBackend in HttpLoading's constructor? – Kilazur Jul 06 '16 at 22:38
0

Ok , I know that may seem trivial, but try to create a variable and initialize it in the constructor

Ruslan
  • 91
  • 1
  • 8
  • I also recommend this -- the optimization engine may be removing the reference to this dependency. – Michael G Jul 06 '16 at 20:14
  • 3
    Besides I don't see how it would make it work, this couldn't be a solution at any level. A service injected in the constructor, should always be available inside the underlying context. – Daniel Pliscki Jul 06 '16 at 20:19
0

To extend @Thierry Templier's answer. I am using Angular v4, and my experience is that you need to provide ALL the dependencies that your extending constructor needs, AND in the right order - I guess it's a legacy way of doing it, from angular 1.x.

My example:

// This is my extended class (relevant part only)

@Injectable()
export class HttpService extends Http {

    constructor(
        backend: ConnectionBackend,
        defaultOptions: RequestOptions,

        private router: Router,
        private loaderService: LoaderService,
        private modalService: ModalService,
        private localStorageService: LocalStorageService
    )
    {
        super(backend, defaultOptions)
    }

// This is the provider factory defined in app.module.ts:

export function httpClientFactory(
        backend: XHRBackend,
        defaultOptions: RequestOptions,
        router: Router,
        loaderService: LoaderService,
        modalService: ModalService,
        localStorageService: LocalStorageService
    ) : Http
{
    return new HttpService(
        backend,
        defaultOptions,
        router,
        loaderService,
        modalService,
        localStorageService
    );
}

This is the configuration (just left the relevant part) in app.module.ts:

providers: [
    ModalService
    LocalStorageService,
    LoaderService,
    {
        provide: HttpService,
        useFactory: httpClientFactory,
        deps: [XHRBackend, RequestOptions, Router, LoaderService, ModalService, LocalStorageService]
    }

Note: notice the order of declaring the deps in the config compared to the factory constructor .. it is the same

wiwi
  • 270
  • 2
  • 9