In software engineering, dependency injection (DI), which is at the heart of Angular Applications, is a technique whereby one object supplies the dependencies of another object. A dependency is considered to be an object that can be used for the same purpose.
- It is a coding pattern in which classes receive their dependencies from external sources rather than creating them.
- An Injection is the passing of a dependency on a dependent object (a client) that would use it.
- Dependency Injection is the heart of Angular Applications.
Whenever we create an Angular Component, it initially asks for an Injector service of the component
- DI (Dependency Injection) is a combination of two terms, i.e., Dependency and Injections
- Dependency: Dependency is an object or service that can be used in any other object.
- Injections: An Injection is a process of passing the dependency to a dependent object. It creates a new instance of the class besides its needed dependencies.
Important Aspects of the DI system in Angular
Injector
Use an injector object to create an instance of a dependency. The injector is a mechanism that passes instantiated objects to the destination one. To create a dependency, an injector looks for a provider.
An Injector maintains a container of service instances that it has created previously. If the container does not exist for that requested service instance, the injector makes one and adds it to the container before returning the service to Angular. When all requested services have been determined and returned, Angular can call the component’s constructor with those services as arguments. This is a dependency injection.
Provider
A Provider registers the dependency object to be injected. Providers are generally singleton (our instance) objects that other objects have access to through dependency injection(DI).
Register Dependency in Two Stages in Angular
1. At App Level
Registering the dependency at the app level creates the injected dependency singleton.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { DIDemoComponent } from './didemo/didemo.component'; import { DiDemoService } from 'src/app/didemo/di-demo.service'; @NgModule({ declarations: [ AppComponent, DIDemoComponent ], imports: [ BrowserModule, AppRoutingModule ], // Register Depedency At App Level providers: [DiDemoService], bootstrap: [AppComponent] }) export class AppModule { }
2. At Component Level
You can also register the dependency at the component level. There is a new instance of the dependency creation.
import { Component } from '@angular/core'; import { DiDemoService } from 'src/app/didemo/di-demo.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], //Inject depedency at component Level providers: [DiDemoService], }) export class AppComponent { title = 'DIDemo'; }
Using the following example, we can see that:
- We are registering the provider of the DIDemo Service with the injector.
- A provider is something that can return or create service, generally the service class itself. We can register providers in Modules or Components
- The Angular Command Line Interface can generate a new DIDemoService class in the src/app/DIDemo folder with the following command:
ng generate service didemo / DIDemo
For DIDemoComponent to get users from DIDemoSevice, it is required to request for DIDemoService to be injected, instead of creating its own DIDemoService instance with new.
We can notify Angular to inject a dependency in a component’s construction by determining a constructor parameter with the dependency type. Here is the DIDemo Component constructor. It asks the DIDemoService to be injected.
DIDemoService
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DiDemoService { userData: any[]; constructor() { this.userData = [ { userId: '001', userName: 'Rahul Suthar' }, { userId: '002', userName: 'Sumir Patel' }, ]; } displayMsg() { return 'Welcome to Dependency Injection Demo Practical'; } }
We can configure injectors with providers at various levels of the app by setting a metadata value in one of these sites:
- The @Injectable() decorator for the service itself
- The @NgModule() decorator for an Ngmodule
- The @Component() decorator for a component
The @Injectable() decorator has the providedIn descriptive over option, where you can define the provider of the decorated service class through the root injector, or over the injector for a particular NgModule.
The @NgModule() and @Component() decorators have the providers’ descriptive data option, where you can set up providers for NgModule-level or component-level injectors.
The @Injectable() decorator tracks it as a service that can be injected. But, Angular cannot exactly inject it everywhere before you configure an Angular Dependency Injector with the provider of that service.
The @Injectable() is an important section in each service definition. The Outside class has been written to expose a displayMsg, which then returns the return statement.
So that we are injecting the dependency, we inject the service DIDemoService in app.component.ts
didemo.component.ts
import { Component, OnInit } from '@angular/core'; import { DiDemoService } from 'src/app/didemo/di-demo.service'; @Component({ selector: 'app-didemo', templateUrl: './didemo.component.html', styleUrls: ['./didemo.component.scss'], //Inject depedency at component Level providers: [DiDemoService] }) export class DIDemoComponent implements OnInit { text: string; users: any[]; constructor(private _didemoservice: DiDemoService) { } ngOnInit(): void { this.users = this._didemoservice.userData; this.text = this._didemoservice.displayMsg(); } }
To display the user data using data binding in the Html file, we will write the following code:
<p>Dependency Injection Demo works!</p> <div> <table> <tr *ngFor="let user of users"> <td>User Id : </td>{{user.userId}} <td>User Name : </td>{{user.userName}} </tr> </table> </div>
Use of Tree-Shakeable Mean in Dependency Injection
What does this mean? If your app doesn’t use the service, then the Angular compiler removes that service from your production build. This results in a smaller, faster application.
Incredibly, you would write a service and not use it. This feature is more helpful for your dependencies. We might be using a component library (like Angular Material) and only use 10% of the services available in that library.
Provide services of that library with tree-shakability in mind. Then, your Angular build could eliminate about 90% of services that your application does not require.
Wrapping Up
Angular’s strengths and use cases are limitless. Once you understand the uses of it, you would probably create fantastic front-ends for many types of developments. The support of modules and light-weight features empower its popularity in the market.