In Angular, the DI framework provides declared dependencies when that class is instantiated. This guide explains how DI works in Angular and how you use it to make your apps flexible, efficient, and robust, as well as testable and maintainable.
Angular Dependency Injection
Dependencies in Angular are services or objects that a class needs to perform its function. Dependency Injection is a coding pattern in which a class asks for dependencies from external sources rather than creating them itself.
Angular Dependency injection is a basic application design pattern. Angular has its dependency injection framework, and you really can’t build an Angular application without it. Moreover, it’s used so widely that almost everyone calls it DI.
First, we will take an example of the scenario where we do not use any Dependency Injection pattern.
Example
Let us take the example of a Computer. The Computer consists of the following things.
- Monitor
- CPU
- Keyboard
So to complete the Computer, we need those three things.
In this example, we need to require four classes to build a fully functional computer.
- Computer class
- Monitor class
- CPU class
- Keyboard class
Without DI(Dependency Injection)
First, create four classes. All Four classes are in the src >> app directory.
Make Monitor.ts class.
// Monitor.ts export class Monitor { public monitorNo = 2; }
Next Keyboard.ts class.
// Keyboard.ts export class Keyboard { public keyboardNo = 1; }
Now, create CPU.ts class.
// CPU.ts export class CPU { public cpuNo = 3; }
Finally, create the Computer.ts class.
// Computer.ts import { Monitor } from './Monitor'; import { CPU } from './CPU'; import { Keyboard } from './Keyboard'; export class Computer { public monitor: Monitor; public cpu: CPU; public keyboard: Keyboard; constructor() { this.monitor = new Monitor(); this.cpu = new CPU(); this.keyboard = new Keyboard(); } public description = 'This Matrix computer'; complete() { return `${this.description} has ` + `${this.monitor.monitorNo} monitors, ${this.cpu.cpuNo} cpus and, ${this.keyboard.keyboardNo} keyboard.`; } }
To complete the Computer class, we need to import all three classes here and make one fully functional Matrix computer.
Now, here we have created three classes instances in the constructor of the Computer class.
Note that the Computer class is dependent on these three classes. Otherwise, it will not complete the Computer.
We are creating instances in the constructor. So monitors, CPUs, and keyboards are not decoupled from the Computer class.
Finally, we need to change the app.component.ts file to work on this example.
// app.component.ts import { Component } from '@angular/core'; import { Computer } from './Computer'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { public computer: Computer; constructor(){ this.computer = new Computer(); } makeComputer(){ return this.computer.complete(); } }
And to change the view, edit the app.component.html
<!--The whole content below can be removed with the new code.--> <div style="text-align:center"> <h1> {{makeComputer()}}!! </h1> </div>
Output
Save this file and type the following command.
ng serve
Example With Dependency Injection(DI)
If we use Dependency Injection, we do not need to create the instances in the constructor.
First, we need to provide all the dependencies to the app.module.ts class.
// app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { Monitor } from './Monitor'; import { CPU } from './CPU'; import { Keyboard } from './Keyboard'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [Monitor, CPU, Keyboard], bootstrap: [AppComponent] }) export class AppModule { }
In the providers array, we need to provide all three dependencies.
Then, In the Computer.ts class, inject those dependencies into Computer’s constructor.
// Computer.ts import { Monitor } from './Monitor'; import { CPU } from './CPU'; import { Keyboard } from './Keyboard'; export class Computer { constructor(public monitor: Monitor, public cpu: CPU, public keyboard: Keyboard) {} public description = 'This Matrix computer'; complete() { return `${this.description} has ` + `${this.monitor.monitorNo} monitors, ${this.cpu.cpuNo} cpus, and ${this.keyboard.keyboardNo} keyboard.`; } }
Finally, modify the app.component.ts file.
// app.component.ts import { Component } from '@angular/core'; import { Computer } from './Computer'; import { Monitor } from './Monitor'; import { CPU } from './CPU'; import { Keyboard } from './Keyboard'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { public computer: Computer; constructor(){ this.computer = new Computer(new Monitor(), new CPU(), new Keyboard()); } makeComputer(){ return this.computer.complete(); } }
So, we have passed the argument at the time of creating a Computer instance.
When a computer instance is created at that time, all the other instances of other classes are also created.
After saving this file, the output will be the same as the previous one.
That’s it for it.