Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you only inject services into services through bootstrap?

I am trying to wire up a basic Angular2 app that uses the Http service. (Most of the tutorials I've seen do this by having a Component consume the Http service, which seems wrong unless the basic philosophy of thin controllers has changed – but that's a different question.)

I would like to create a service that uses Angular's Http service. But I can't figure out how to inject the Http service other than this:

boot.ts:

import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {HTTP_PROVIDERS } from 'angular2/http';

bootstrap(AppComponent, [HTTP_PROVIDERS]);

myService.ts:

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';

@Injectable()
export class aService{
    constructor(http:Http){
    }
    /** do some stuff *//
}

This works, but it seem very wrong to require the user of the service to know the service's dependencies and be required to inject them into the bootstrap process. It seems like there should be a way to directly hand a providers array to a service the same way you can a component, but I can't find it. Am I just missing something?

like image 247
Mark Avatar asked Jan 03 '16 21:01

Mark


1 Answers

Update

This way if a parent injector provides an implementation for OtherService this one is used, otherwise OtherServiceImpl is used (default).

@Injectable()
class SomeService {
  OtherService _other;

  SomeService(Injector injector) {
    _other = injector.getOptional(OtherService);
    if (_other == null) {
      _other = injector.resolveAndCreateChild([
        provide(OtherService, useClass: OtherServiceImpl)
      ]).get(OtherService);
    }
    _other.doSomething();
  }
}

If you provide another one like

bootstrap(AppElement, [
  provide(OtherService, useClass: OtherServiceImpl2)
]);

OtherServiceImpl2 is used.

See also https://github.com/angular/angular/issues/5622

Original

You could just make the http service optional (using the @Optional() annotation) and if none is provided just create an instance inside the constructor with new Http(). This way the user doesn't need to know about the services dependencies, but is able to pass alternative implementations if necessary (for example for testing). If creating the dependeny inside the service requires DI itself, you can inject an injector and use it to get dependencies. See also optional dependencies in http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html

What also could work (not tried myself yet) is just to create a child injector and instruct it to skip self

From the SkipSelfMetadata documentation

  class Dependency {
  }

  @Injectable()
  class NeedsDependency {
    dependency;
    constructor(@SkipSelf() dependency:Dependency) {
      this.dependency = dependency;
    }
  }

  var parent = Injector.resolveAndCreate([Dependency]);
  var child = parent.resolveAndCreateChild([NeedsDependency]);
  expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);

  var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
  expect(() => inj.get(NeedsDependency)).toThrowError();

I don't know yet if this still resolves from "self" if parent can't provide the requested type.

like image 161
Günter Zöchbauer Avatar answered Sep 30 '22 04:09

Günter Zöchbauer