1
votes

As documentation says

Dependency Injection is a powerful pattern for managing code dependencies

But does Angular's DI has any sense?

Let's say we have an HeroComponent living in the HeroModule, and it uses HeroService. To use it in HeroComponent we have to:

  1. Import HeroService in HeroModule
  2. Inject HeroService in module providers
  3. Import HeroService in HeroComponent
  4. Add type or add injected service to component's parameters.

Why don't just

  1. import HeroService into HeroComponent?

We can export its instance so we can still have a singleton across our app and we can configure our webpack/karma to be still able to import mocks instead of real implementations inside our tests.

Someone can say Angular's DI makes architecture less tightly coupled, but is it true? In Angular 1.x indeed you just specified params in the constructor. But in Angular2 you have to import your dependency additionally, and you have to specify from where. So where is this loosely coupling?

Example:

import { Http } from 'angular2/http';

export class Login {
  constructor(http: Http) {
    http.whatever()
  }
}

To be able to execute our injection, we have to import it. And when we import, we define exactly what service we are going to use. I don't see any difference from above and below example:

import { http } from 'angular2/http'; (instance)

export class Login {
  constructor() {
    http.whatever()
  }}
1
Of course, IoC is a popular pattern and good practice as it's one of SOLID principles. But it's not IoC for me if you have to manual import your dependency additionally.Oskar Kamiński
There is some limitation that in TS you can't use interfaces for DI, but you can still use (abstract) base classes (that's a TS limitation, not an Angular2 limitation). I don't think importing is otherwise related to the IoC topic at all. If you want to use a type in your source code for whatever reason, you have to import it, that's not related to DI.Günter Zöchbauer
" And when we import, we define exactly what service we are going to use." That's just wrong. If you add providers: [{provide: Http, useClass: MyFancyHttp}] to @NgModule() then you get something different than what you imported.Günter Zöchbauer
You define this providers inside module and when you want to inject it inside your component, you add constructor(http: Http) and you have to get this Http from somewhere. So you have to import it. From where?Oskar Kamiński

1 Answers

1
votes

Dependency injection is not about importing. It's about having the instances created for you outside of your components. Without DI your HeroComponent would still need to add the import statement for the HeroService and you'd need to manually instantiate it inside HeroComponent:

myHeroService: HeroService = new HeroService();

Now imagine you need to use this service in three components. Then you'll need to write the above line in three different places.

What if you need to use a mock service because the real one is not ready yet (or you want to have a mock service for unit tests)? Without DI you'd need to modify the code in three components to something like this:

myHeroService: MockHeroService = new MockHeroService();

With DI each of the three components would have the same constructors that you'd never modify in the above scenario, e.g.:

class HeroComponent{

   constructor(heroService: HeroService){}
}

Switching to the MockHeroService would require only one change (the provider) in the HeroModule declaration:

@NgModule({
...
providers:[provide: HeroService, useClass: MockHeroService]
})

The above code would instruct Angular to create a singleton for the entire module (app). If you decide to introduce more than one instance of HeroService (say HeroComponent1 uses HeroService, and HeroComponent2 uses MockHeroService), then just declare providers:[HeroService] in the HeroComponent1:

@Component({
...
providers:[HeroService]
})

Now Angular will create a MockHeroService instance for the entire app and another instance of the HeroService for the HeroComponent1 and its children.

Keep in mind that you wouldn't need to change the constructors' signatures in any components that use this service.

Now imagine that you'd decide that in one component you want to introduce a factory function that would conditionally instantiate either HeroService or MockHeroService. You'd just declare a provider for such component using the useFactory syntax.

I can keep going :)