Introduction

Dependency Injection (DI) is a design pattern used to implement IoC (Inversion of Control), allowing a class to receive its dependencies from an external source rather than creating them itself. In Angular, DI is a core concept that enables efficient management of dependencies, making your code more modular, testable, and maintainable.

Key Concepts

  1. Dependency: An object that another object depends on.
  2. Injector: A service locator that provides dependencies to classes.
  3. Provider: An object that tells the injector how to obtain or create a dependency.
  4. Token: A key used to map a dependency to a provider.

How Dependency Injection Works in Angular

  1. Registering Providers: You register providers in Angular's dependency injection system, typically in the @NgModule decorator or in the @Injectable decorator.
  2. Injecting Dependencies: Angular injects the dependencies into the constructor of the class where they are needed.

Practical Example

Step 1: Creating a Service

First, let's create a simple service that we will inject into a component.

// src/app/logger.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class LoggerService {
  log(message: string) {
    console.log(message);
  }
}

Step 2: Injecting the Service into a Component

Next, we will inject this service into a component.

// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { LoggerService } from './logger.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  constructor(private logger: LoggerService) {}

  ngOnInit() {
    this.logger.log('AppComponent initialized');
  }
}

Step 3: Registering the Service

In this example, the LoggerService is provided in the root injector by using the providedIn: 'root' syntax in the @Injectable decorator. This means the service is available application-wide.

Common Mistakes and Tips

  1. Forgetting to Register the Service: Ensure that your service is registered either in the @NgModule providers array or using providedIn: 'root' in the @Injectable decorator.
  2. Circular Dependencies: Be cautious of circular dependencies, where two or more services depend on each other, leading to an infinite loop.
  3. Singleton Services: By default, services provided in the root injector are singletons. If you need multiple instances, provide the service in a component or a module.

Exercise

Task

Create a new service called DataService that fetches data and inject it into a component to display the data.

Solution

  1. Create the Service
// src/app/data.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  getData() {
    return ['Data 1', 'Data 2', 'Data 3'];
  }
}
  1. Inject the Service into a Component
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  data: string[];

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.data = this.dataService.getData();
  }
}
  1. Display the Data in the Template
<!-- src/app/app.component.html -->
<ul>
  <li *ngFor="let item of data">{{ item }}</li>
</ul>

Conclusion

In this section, we covered the basics of Dependency Injection in Angular, including how to create and inject services. We also discussed common mistakes and provided a practical exercise to reinforce the concepts. Understanding DI is crucial for building scalable and maintainable Angular applications. In the next section, we will delve into Hierarchical Injectors, which allow for more advanced dependency management.

© Copyright 2024. All rights reserved