Introduction to Dependency Injection in Angular

Tutorial 5 of 5

Introduction

This tutorial aims to guide you through the concept of Dependency Injection (DI) in Angular. Angular's DI is a design pattern in which a class receives its dependencies from external sources rather than creating them itself. This makes your code more reusable, modular, and easier to test.

By the end of this tutorial, you will:

  • Understand what Dependency Injection is and why it's useful
  • Learn how to implement DI in Angular
  • Be able to make your code more modular and easily testable

Prerequisites:
Familiarity with Angular and Typescript will be beneficial.

Step-by-Step Guide

In Angular, DI is implemented by the @Injectable() decorator. When a class is marked with this decorator, Angular knows that it could be used as a provider, i.e., a source of dependencies for other classes.

When a class needs a service, instead of directly creating an instance of the service, it will ask the Angular injector. If the injector can find the service, it will inject it; otherwise, it will throw an error.

Best Practices:

  1. Use the @Injectable() decorator for any service that you want to be injectable.
  2. Always define the metadata { providedIn: 'root' } for services that should be singleton (only one instance for the entire app).
  3. Try to inject dependencies in the constructor.

Code Examples

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

@Injectable({
  providedIn: 'root',  // This service is available throughout the app
})
export class AppService {
  getMessage(): string {
    return 'Hello from AppService!';
  }
}

In this code snippet, we define a service AppService with a method getMessage().

// app.component.ts
import { Component } from '@angular/core';
import { AppService } from './app.service';

@Component({
  selector: 'app-root',
  template: `<h1>{{ message }}</h1>`,
})
export class AppComponent {
  message: string;

  constructor(private appService: AppService) {
    this.message = this.appService.getMessage();
  }
}

In AppComponent, we inject AppService in the constructor and use it to get a message.

Summary

In this tutorial, we have learned about Dependency Injection (DI) in Angular, why it's beneficial, and how to implement it to make our code more modular, reusable, and testable. We've seen how the @Injectable() decorator is used to make a service injectable and how to inject services into components using the constructor.

For further reading, check out the official Angular documentation on Dependency Injection.

Practice Exercises

  1. Exercise 1: Create a LoggerService with a method log(message: string). Inject it into AppComponent and call the log method.
  2. Exercise 2: Update the LoggerService to include a logError(message: string) method. Update AppComponent to use this new method.

Solutions:

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

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

  logError(message: string) {
    console.error(message);
  }
}
// app.component.ts
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';

@Component({
  selector: 'app-root',
  template: `<h1>Hello, world!</h1>`,
})
export class AppComponent {
  constructor(private logger: LoggerService) {
    this.logger.log('Hello from AppComponent');
    this.logger.logError('This is an error message');
  }
}

Keep practicing and exploring other Angular concepts!