Search code examples
inheritanceangularangular2-http

No provider for ConnectionBackend while inheriting from Http


I am trying to make a wrapper around built in Http service in angular 2 to have a possibility to add custom behaviour (headers, parameters etc.)

So I created a usual class (not a service) and inherit it from Http. Class definition

import {
  Http,
  ConnectionBackend,
  RequestOptions,
  RequestOptionsArgs,
  Headers
} from '@angular/http';
import { Observable } from 'rxjs/Observable';
import {tamamApiUrl} from '../constants';
import {CustomQueryEncoder} from './CustomQueryEncoder';
import 'rxjs/Rx';

export class BaseHttp extends Http {
  protected applicationDataService;
  protected baseUrl: string = tamamApiUrl;
  protected encoder: CustomQueryEncoder;

  constructor(backend:ConnectionBackend,
              defaultOptions: RequestOptions, applicationDataService: any) {
    super(backend, defaultOptions);
    this.applicationDataService = applicationDataService;
  }


  get(url: string, options?: RequestOptionsArgs): Observable<any> {
    this.addDefaultOptions(options);
    return super.get(url, options);
  }

  post(url: string, body: any, options?: RequestOptionsArgs): Observable<any> {
    this.addDefaultOptions(options);
    this.addDefaultPostOptions(options);
    return super.post(url, body, options);
  }

  private addDefaultOptions(options?: RequestOptionsArgs): RequestOptionsArgs {
    if (options == null) {
      options = new RequestOptions();
    }
    if (options.headers == null) {
      options.headers = new Headers();
    }
    const applicationData = this.applicationDataService.getApplicationData();

    if (applicationData.applicationKey) {
      options.headers.append('application-id', applicationData.applicationKey);
    }

    if (applicationData.secretKey) {
      options.headers.append('secret-key', applicationData.secretKey);
    }

    if (applicationData.userToken) {
      options.headers.append('user-token', applicationData.userToken);
    }

    return options;
  }

  private addDefaultPostOptions(options): void {
    options.headers.append('Content-Type', 'application/json');
  }

  /*private requestInterceptor(): void {
    this.loaderService.showPreloader();
  }


  private responseInterceptor(): void {
    this.loaderService.hidePreloader();
  }*/
}

Than I Created a service which inherited from this class so that I can inject it later and use for my purposes.

import { Headers, URLSearchParams } from '@angular/http';
import {tamamRootUrl} from '../constants';
import {BaseHttp} from '../api/BaseHttp';
import { Injectable } from '@angular/core';
import {
  ConnectionBackend,
  RequestOptions,
} from '@angular/http';
import {ApplicationService} from './ApplicationService';

@Injectable()
export class InspectionHttpService extends BaseHttp{

  protected baseUrl: string = tamamRootUrl;
  protected params: URLSearchParams = new URLSearchParams();

  constructor(backend: ConnectionBackend,
              defaultOptions: RequestOptions, protected applicationService: ApplicationService) {
    super(backend, defaultOptions, applicationService);
  }

  getRootUrl() {
    return this.baseUrl;
  }
}

Service definition

After I try to inject created service in component I receive an error:

error_handler.js:47 EXCEPTION: Uncaught (in promise): Error: Error in ./VehiclesListComponent class VehiclesListComponent_Host - inline template:0:0 caused by: No provider for ConnectionBackend!

I tried to search solution using stack-overflow but it see,s that HtpModule should already have all necessary for a proper work.

Could anyone help me? Where is the problem?


Solution

  • You can't just add it to the providers like this

    providers: [ ApplicationService, InspectionHttpService ]
    

    If you do, then Angular will try to create it, and it won't find a provider for ConnectionBackend

    You need to use a factory, where you can just pass the XHRBackend (which implements ConnectionBackend)

    imports: [HttpModule],
    providers: [
      ApplicationService,
      {
        provide: InspectionHttpService,
        deps: [XHRBackend, RequestOptions, ApplicationService],
        useFactory: (backend, options, aplicationService) => {
          return new InspectionHttpService(backend, options, applicationService);
        }
      }
    ]
    

    With this, you can inject it as InspectionHttpService.

    constructor(private http: InspectionHttpService) {}
    

    If you want to be able to inject it as Http, then you need to change the provide to Http instead of InspectionHttpService. But this overrides any ability to use the regular Http if you ever need it.

    UPDATE

    In some environments, it's possible you may get an error with the above code. I forgot the exact error message, but it will give you a proposed solution of extracting the factory function, i.e.

    export function httpFactory(backend: XHRBackend, options: RequestOptions,
                                service: ApplicationService) {
      return new InspectionHttpService(backend, options, service);
    }
    
    useFactory: httpFactory
    

    From what I remember, I think the error has something to do with AoT