Angular 4 Services (Http) Inheritance

Inheriting in Angular 4, meaning Typescript combined with Dependency Injection, can be tougher than you would expect.

lets say we just want a base class for all of our Services, they all use a WebApi, so they have their base url, which always bring the "all" array, and then "\3" for each item. so they all look like this:

export class ServiceBaseClass {
  constructor(private baseUrl, private http:Http) {}
  //public functions using baseUrl and http like
  Get(itemID = ""){
    var url = this.baseUrl + itemID;
    var promise = new Promise ((resolve, reject) => {
      this.http.get(url); // and whatever
    })
  }
}

so you would like to make this the base class and each service just declaring its own "All Items" array and url. so you would thing this would work:

export class ChocolateService extends ServiceBaseClass {
  constructor() {
    super('http://chocolate/');
    this.Get();
  }
}

HELL NO!

1st error:


ok, so lets move  private http:Http  outside the CTOR, ok, we got it how is should be done with TypeScript, the project builds.

but now you should start getting Angular errors such as, 1st of all

"ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'get' of undefined

TypeError: Cannot read property 'get' of undefined"

that simply means, and you should see your line of code using "http.get", that "http" is undefined.

so how do we create a new instance of http? and why with the DI (Dependency Injection) it worked?

well DI works in a way that whatever you throw into your CTOR it will resolve it by itself, but if you need it somewhere else you sometimes need to work hard, like with Http.

so if you try to make new Http(), as optional parameter or private, you'll end up doing this:

import { Http, Response, Headers, RequestOptions, ResponseOptions } from '@angular/http';
import { XHRBackend, ConnectionBackend, BrowserXhr, XSRFStrategy, BaseResponseOptions, CookieXSRFStrategy, BaseRequestOptions } from '@angular/http';

private http:Http = new Http(
  new XHRBackend(
    new BrowserXhr(), new ResponseOptions(), new CookieXSRFStrategy()),
  new RequestOptions()
);

that is your 1st option.

2nd is to use the Injector  ReflectiveInjector  like this (same imports)

private http:Http;
constructor(private baseUrl) {
  this.http = ReflectiveInjector.resolveAndCreate([
    Http, BrowserXhr,
    { provide: ConnectionBackend, useClass: XHRBackend },
    { provide: ResponseOptions, useClass: BaseResponseOptions },
    { provide: XSRFStrategy, useFactory: () => new CookieXSRFStrategy },
    { provide: RequestOptions, useClass: BaseRequestOptions }
  ]).get(Http);
}

will give the same results.

NOTICE with RC6 you must use  useFactory: () => new CookieXSRFStrategy  and now useClass, and anyway, the point is that you must work hard a little for a private, base Http.


-------------------------------------


Yet another option, that sometimes is the better one, is to create a Manager Class, such as this:

export class RequestManager {

public baseUrl;
constructor(@Inject(Http) private http:Http) { }

if you dont use @Inject nor @Injectable you should get

Uncaught Error: Can't resolve all parameters for RequestManager: (?).

and then use in in your service CTOR like this:

constructor(public requestManager:RequestManager) {
requestManager.baseUrl ='http://chocolate/';

but then you'll see yet another error:

ERROR Error: No provider for RequestManager!

this is because you need to register it as a provider. you can register a provider to an  @NgModule  or  @Component  . so for the services you should use your  AppModule .

providers: [ RequestManager ],


Enjoy!

Comments

Popular posts from this blog

c# Service Play Sound with NAudio example by Moshe

Asp.Net Ending Response options, Response.End() vs CompleteRequest()

JS/JQ simulate Enter event