Skip to content
View lessons Login
3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Understanding Dependency Injection in Angular

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){}
}

or this:

export class AppComponent {
private myService = inject(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.

Technically, the private keyword is only required for the inject syntax if you want the class member to specifically be private.

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? Why not just create new instances ourselves? Some benefits are:

  1. 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)
  2. 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.
  3. 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.

Understanding Tokens and Providers

We have already seen how dependency injection works:

export class AppComponent {
constructor(private myService: MyService){}
}

It seems we just add a parameter to the constructor, and give that parameter a type of whatever we want Angular to inject for us. Give it a type of MyService and we will get an instance of MyService.

This is an accurate enough understanding, but let’s dive a little bit deeper. What we are doing here is supplying a type token (we can also use string tokens but this is not as common). You might think that Angular knows what MyService is because we would import that at the top of our file:

import { MyService } from './shared/data-access/my-service';
// ...snip
export class AppComponent {
constructor(private myService: MyService){}
}

It seems sensible to think that Angular will just go find MyService from that file and create a new instance of it, but that isn’t exactly how it works. This type is being used as a token and what that token represents depends on how it is provided.

Providing Tokens

One aspect of components and modules that we have not looked at yet is the providers array, and this is relevant to how tokens are provided. First of all, manually providing tokens is not nearly as common as just doing this:

@Injectable({
providedIn: 'root'
})
export class MyService {}
STANDARD STANDARD

Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).