Understanding Dependency Injection in Angular
Dependency injection is another concept that is not unique to Angular, it is a general programming pattern. In the context of Angular, the idea is that instead of creating instances of classes ourselves, e.g:
export class AppComponent {
myService = new MyService();
}
Angular will handle creating new
instances of our classes, and provide them to us through dependency injection which looks like this:
export class AppComponent {
constructor(private myService: MyService){}
}
Angular will use the MyService
type here as an InjectionToken
. This is how Angular knows we want an instance of MyService
. The instance of MyService
is assigned to myService
and then we can use it. By using the private
or public
keyword, this will make myService
available as a class member to the entire class, rather than just being available within the constructor
.
NOTE: Using the private
keyword means it will only be available within the class. If you need it to also be available to the template for this class you can use the public
keyword.
In both of the examples above, we now have an instance of MyService
that is available on this.myService
throughout the class.
Why Dependency Injection?
Why do this in Angular? At least in my view, this gives us two main benefits:
- It makes automated testing easier as we can easily provide fake versions of dependencies instead of real ones (automated testing is a bit more advanced and not something we actually cover in this course)
- It allows us to share instances of our objects more easily. This way we can have two separate components share the same single instance of a service, which means they will be able to share data, or we could create two separate instances of the service if we prefer.
- It makes creating instances much easier, as we don’t need to pass all of the dependencies that the service itself depends on.
That last one is a bit less clear, so let’s expand on that. Imagine that MyService
also has two dependencies of its own that it needs injected:
export class MyService {
constructor(http: HttpClient, someOtherService: SomeOtherService){}
}
To do its job, MyService
also needs an instance of HttpClient
and SomeOtherService
. If we are using Angular’s dependency injection system then we can just do this to inject MyService
somewhere else in our app:
export class AppComponent {
constructor(private myService: MyService){}
}
But if we are not using dependency injection, then we need to pass all of the MyService
dependencies manually like this:
export class AppComponent {
myService = new MyService(new HttpClient(), new SomeOtherService())
}
Imagine doing this several times throughout your application, and then we need to update MyService
to have a third dependency:
export class MyService {
constructor(http: HttpClient, someOtherService: SomeOtherService, anotherService: AnotherService){}
}
Now we would need to go back and update every instance of MyService
we are creating:
export class AppComponent {
myService = new MyService(new HttpClient(), new SomeOtherService(), new AnotherService())
}
Perhaps you can see how this would become a massive pain.