Angular Dependency Injection: In-depth
Published: 2022-02-20 22:14:55

###Introduction###

Angular has its build-in dependency injection system with most powerful and standout features.

Most of us might have already been using this feature and don't even realize it, thanks to its implementation by Angular team. But understanding this system in depth would help us from architectural perspective along with below:

- Solve some weird dependency injection errors

- Build applications in more modular way

- Configure some third party libraries with customizable services

- Implementing tree-shakable features

###Dependency Injection###

So what exactly is this dependency injection?

_It is a technique in which an Object receives other Objects that it depends on_, and is often used pretty much everywhere in modern applications irrespective of tech stack. 

So for example, you write a service that gets data from backend, you would certainly need an HTTP service to request and there could be other similar dependencies.

So you might end up creating your own dependencies in the service, like shown below:

_items.service.ts_:
```javascript
export class ItemsService() {

  http: HttpClient;
  
  constructor() {
    this.http = new HttpClient(httpHandler);
  }
}
```

At first this might look like a straight forward solution, but as the time goes on, it would be very hard to maintain and test.

> Notice that this class knows not only how to create its dependencies, but it also knows about the dependencies of the `HTTPClient` class as well.

Try to compare this that leverages angular dependency injection:

_items.service.ts_:
```javascript
@Injectable()
export class ItemsService() {

   http: HttpClient;

   constructor(http: HttpClient) {
     this.http = http;
   }
}
```

This version, does not need to create its dependencies, it just receives those as input arguments from constructor.

Now lets try to setup dependency injection step by step deep dive into it. Hands on time!

Lets start this by creating a simple class:

_items.service.ts_:
```javascript
export class ItemsService() {

  http: HttpClient;

  constructor(http: HttpClient) {
    this.http = http;
  }
...
}
```

Now lets try to use this class in different part of application. For our understanding, lets assume we are using this in `ItemsComponent`:

```javascript

export class ItemsComponent  {

  constructor(private ItemsService: ItemsService) {
    ...
  }
  ...
}
```

> Notice that this class is not yet linked to Angular dependency injection system, so Angular does not know at this point a way to get `ItemsService` instance in order to provide to `ItemsComponent`. So we get a famous error!

      NullInjectorError: No provider for ItemsService!

 
Now, if you inspect this error, you understand that Angular is looking for something known as `provider`.

###Dependency Injection Providers###

The error `No provider` means simply that Angular cannot initialize a given dependency(`ItemsService` in our case) and provide to `ItemsComponent` because it does not know how to create it.

To do that, Angular needs to know what is known as a provider factory function. 

Do not get tripped by fancy names, end of the day, it is nothing but _a simple function_ that returns an instance of your dependency(`ItemsService `in our case).

> The point to understand here is that for every single dependency in your application, be it a service or a component, there is somewhere a plain function that is being called and that function returns your dependency instance.

This provider factory function can be automatically created by Angular for us with some simple conventions. But we can also provide that function ourselves. 

Lets create one ourselves to really understand what a provider is.

#####Create our own provider factory function#####

```javascript
function ItemsServiceProvider(http:HttpClient): ItemsService {
  return new ItemsService(http);
}
```

Like I said earlier, this is just a plain function that takes as input any dependencies that `ItemsService `needs. And it will then returns `ItemsService` instance.

Now if you think on highlevel of what we have done, we have got a service thats being used in a component. And we have created a provider factory function that returns instance of `ItemsService`. 

> So at this point, Angular yet to understand that we wrote this provider factory function(item 1). 

> More important than that, we need to tell Angular that it needs to use this function whenever it has to provide `ItemsService` instance(item 2).

So lets dive in to Injection Tokens to make a link between provider factory function that we wrote and `ItemsService`.
###Item 1###
#####Injection Tokens#####

In order to uniquely identify dependencies, we can define what is know as an Angular Injection Token. Again, dont get tripped by fancy names, its just an unique identifier for Angular to link our `ItemsService`.

```javascript
export const ITEMS_SERVICE_TOKEN = 
new InjectionToken<ItemsService>("ITEMS_SERVICE_TOKEN");
```

You can actually use any unique value but to save you from that effort Angular added `InjectionToken `class, It just returns an unique token Object.

Now that we have an unique object to identify dependencies, how do we use it?

#####Manually configure a provider#####
Now that we have provider factory function and injection token, we can configure a provider in the Angular dependency injection system, that will know how to create instances of `ItemsService` if needed.

We can do this in module as shown below:
 
_items.module.ts_:
```javascript
@NgModule({
  imports: [ ... ],
  providers: [
      {
      provide: ITEMS_SERVICE_TOKEN,
      useFactory: itemsServiceProvider,
      deps: [HttpClient]
    }
    ]
})
export class ItemsModule { }
```

As you see, manually configured provider needs 

- `provide`: An injection token of dependency type(ItemsService in our case) helps Angular determine when a given provider factory function should be called or not.

- `useFactory`: provider factory function. Angular will call when needed to create dependencies and inject them.

- `deps`: dependencies that are needed for provider factory function. HTTP client in our case. 

###Item 2###

So now Angular knows how to create instances of `ItemsService` right? But if you run app, you might be surprised to see that same `NullInjectorError`

Well, that is because we are yet to tell Angular that it should use our items provider to create `ItemsService `dependency.

We can do this just by using `@Inject` annotation where-ever `ItemsService `is being injected.

_items.component.ts_:
```javascript
@Component({
  selector: 'Items',
  templateUrl: './Items.component.html',
  styleUrls: ['./Items.component.css']
})
export class ItemsComponent  {

  constructor( @Inject(ITEMS_SERVICE_TOKEN) private itemsService: ItemsService) {
    ...
  }
  ...
}
```

Ok, we have done a lot so far, lets recap whats happening at this point.
The explicit use of `@inject` decorator allows us to tell Angular that in order to create this dependency, it needs to use the specific provider linked to the `ITEMS_SERVICE_TOKEN` injection token.

This token contains unique information that identifies a dependency type from the point of view of Angular, and this way Angular knows that it needs to use ItemsService Provider factory function. 

It goes ahead and does just that. With this our application should now work correctly with no more errors!

Great, but have you ever thought:

#####Why don't I usually configure providers?#####

You are right, you usually dont have to do all this to configure provider factory functions or injection tokens yourself. Angular takes case of all that for  you in the background.

> Angular always automatically creates a provider and an injection token for us under the hood


To better understand this, let's simplify our provider and after few iterations, you would reach to something that you are much more used to.

####Iteration 1####
Lets see the providers that we have.

```javascript
providers: [
      {
      provide: ITEMS_SERVICE_TOKEN,
      useFactory: itemsServiceProvider,
      deps: [HttpClient]
    }
    ]
```

As we discussed earlier, injecton token is just for Angular to uniquely identify. Just like an ID field in database. So you can simplify this by just using class name. 

> A class name can then be uniquely represented at runtime by its constructor function, and because it's guaranteed to be unique, it can be used as an injection token.

```javascript
@NgModule({
  imports: [ ... ],
 
  providers: [
      {
      provide: ItemsService,
      useFactory: itemsServiceProvider,
      deps: [HttpClient]
    }
    ]
})
export class ItemsModule { }
```

As you can see, we are no longer using ITEMS_SERVICE_TOKEN injection token to uniquely identify dependency type. And using the class name itself to identify the dependency type.

If you run your app, you would get an error. Well if you think about it, we have removed ITEMS_SERVICE_TOKEN here, but we are yet to change it in the `ItemsComponent`

_items.component.ts_:
```javascript
export class ItemsComponent  {

  constructor( @Inject(ItemsService) private itemsService: ItemsService) {
    ...
  }
  ...
}
```
And with this, everything should start working again.

####Iteration 2####

We wrote a provider factory function with `useFactory`. Instead of that we have other ways to tell Angular on how to instantiate a dependency.

We can use `useClass `property.

This way, Angular will know that the value we are passing is a valid class. So basically Angular just simply calls it using `new` operator.
_items.module.ts_:
```javascript
@NgModule({
  imports: [ ... ],

  providers: [
      {
      provide: ItemsService,
      useClass: ItemsService,
      deps: [HttpClient]
    }
    ]
})
export class ItemsModule { }
```

Now this greatly simplifies our provider as we do not need to write a provider factory function manually.

####Iteration 3####

Along with this convenient feature of `userClass`, Angular will try to infer the injection token at runtime. So we dont even need the `inject` decorator in the component anymore.
_items.component.ts_:
```javascript
export class ITemsComponent  {

  constructor(private itemsService: ItemsService) {
    ...
  }
  ...
}
```
####Iteration 4####
Instead of defining a provider object manually in modules, we can simply pass the name of class.
_items.module.ts_:
```javascript
@NgModule({
  imports: [ ... ],

  providers: [
    ItemsService
  ]
})
export class ItemsModule { }
```

With this Angular under hood will infer that this provider is a class, it will then create a factory function and create a instance of the class on demand.

All this happens just by the class name. This is what you would see more often which is super simple and easy to use.

As you can see, Angular made this super simple and you won't even realize that there are providers and injection tokens stuff happening behind the scenes.

####Iteration 5####
Remember the `deps` property? Now that we have removed our manually created provider object with just class name, Angular will not know how to find the dependencies of this class.

To let Angular know, you also need to apply Injectable() decorator to the service class

```javascript
@Injectable()
export class ItemsService() {

   http: HttpClient;

   constructor(http: HttpClient) {
     this.http = http;
   }
...
}
```

This decorator will tell Angular to find the dependencies for this class at runtime!

Generally this is how we use the Angular Dependency Injection system without even worrying about all the stuff that is happening under the hood.

####Angular Dependency Injection Hierarchy####

So far we have defined our providers in `modules` ie `ItemsModule` in our case. But these providers can be defined in other places as well.

- At component

- At directive

- At module(we have already done that)

So lets see the differences between defining a provider at each place and its impact.

If you need a dependency somewhere, for example if you need to inject a service into a component, Angular is going to first try to find a provider for that dependency in the list of providers of that component.

If Angular doest find it there, then Angular is going to try to find provider in parent component. If it finds, it will instantiate and use it. But if not, it will check parent of parent component and so on.

If it still doesnt find anything, Angular will then start with the providers of the current module, and look for a matching provider.

If it doesn't find it, then Angular will try to find a provider with the parent module of the current module, etc. until reaching the root module of the application.

If no provider is found even at root module, we get our famous error _No provider found_ 

This hierarchical dependency injection system allows us to build our applications in a much more modular way. With this we can isolate different parts of out application and can make them to interact only if needed.

There are some implications to this if you don't understand how all this works. 
Lets get to hands on again! to see what am talking about.


####Hands on for Hierarchical Dependency####


Lets add some unique identifier inside our service.
_items.service.ts_:
```javascript
let counter = 0;

@Injectable()
export class ItemsService() {

  constructor(private http: HttpClient) {
      counter++;
      this.id = counter;
  }
...
}
```

Now lets create a simple parent child components and inject `ItemsServcice` in multiple paces and see what happens.

Parent component is `AppComponent`
Child component is `ItemComponent`

So assume we need to display list of all items. Below is the template

_app.component.html_:

```html
<div>
    <item *ngFor="let item of  items"
                 [item]="item">
    </item>
</div>
```

Here is the component class

_app.component.ts_:

```javascript
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [ ItemsService ]
})
export class AppComponent {

  constructor(private itemsService: ItemsService) {
    console.log(`App component service Id = ${itemsService.id}`);
  }
...
}
```

Notice that we are adding ItemsService to the providers of the `AppComponent`


Lastly, below is our `ItemComponent`

_Item.Component.ts_:

```javascript
@Component({
  selector: 'item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css'],
  providers: [  ItemsService ]
})
export class ItemComponent {

  constructor(private itemsService: ItemsService) {
    console.log(`items service Id = ${itemsService.id}`);
  }
}
```

Notice that we are adding ItemsService to the providers of this component as well.

Now, lets run our application and assume we have 3 items and guess what happens?

```
App component service Id = 1
items service Id = 2
items service Id = 3
items service Id = 4
```

As you see, unlike the `app component`, `Item component` looks like getting new instance of `ItemsService` every time.

> Each instance of the `ItemComponent` needed a `ItemsService`, it tried to intantiate it by looking at its own list of providers. Each of item component instance found a matching provider in its own providers list. So it uses it to create a new instance each time.

In most of the applications, the services tent to be written stateless. So there is really no need to create so many instances of it. Just one instance should be good enough.

So if we just remove providers in this component, Angular will reach to parent component for `ItemsService `instance.
 
_item.component.ts_:
```javascript
@Component({
  selector: 'item',
  templateUrl: './item.component.html',
  styleUrls: ['./item.component.css']
})
export class ItemComponent {

  constructor(private itemsService: ItemsService) {
    console.log(`items service Id = ${itemsService.id}`);
  }
...
}
```

Now if we run our application you should just see one instance of the service

```
App component service Id = 1
items service Id = 1
items service Id = 1
items service Id = 1
```

###Tree-Shakeable Providers###

To explain in simple terms, Including only necessary code in the final bundle and removing un-used code is called tree shaking.

Assume we have two modules, a root module(`AppModule`) and a feature module(`ItemsModule`). Our `ItemsService` would be defined under `ItemsModule`.

So if we need to use some component from `ItemsModule`, we would have to import `ItemsModule `in `AppModule`. But for some reason, in the `AppModule` we ended up never using `ItemsService`. In this case we dont want to include `ItemsService `in the final bundle.

In order to achieve this we have to remove the `ItemsService `reference from the list of providers of the `ItemsModule`. 

```javascript
@NgModule({
  imports: [ ... ]
})
export class ItemsModule { }
```

But then if we do that, dont we get "provider not found" error?

Yes we would. because there is no provider. Here is how we can define a `provider `without importing it in the `module `file.

_items.service.ts_

```javascript
@Injectable({
  providedIn: ItemsModule
})
export class ItemsService() {

  constructor(private http: HttpClient) {

  }
...
}
```
As you can see we just flipped the order of dependencies. This way `ItemsModule` will not import `ItemsService`.
So if anyone who is importing `ItemsModule`, and is not using `ItemsService`, code related to ItemsService will not be included in the final bundle.


Using providedIn, we can not only define module-level providers, but we can even provide service to other modules, by making the services available at the root of the module dependency injection tree.
_items.service.ts_:
```javascript
@Injectable({
  providedIn: "root"
})
export class ItemsService() {

  constructor(private http: HttpClient) {

  }
...
}
```
This is the most common syntax that you would be seeing in day to day of Angular developer.

With this syntax, the `ItemsService` is now an application wide singleton, meaning there would always be only one instance of it.

####Conclusion####
There is lot going on behind the scenes that is making things easier for us related to dependency injection. But knowing how the system works in detail will be helpful in making key decisions in you applications architecture. Thanks for reading. If you have some questions, corrections that I might have to make in the article or comments please let me know in the comments below and I will get back to you.