DEV Community

Cover image for The ABCs of Angular : Dependency Injection Part 1
Ismail Labbi
Ismail Labbi

Posted on

The ABCs of Angular : Dependency Injection Part 1

What is Dependency Injection?

Let’s start with an example to better understand 😃.

I have a LoggerService class that implements a log method. This method helps us display messages:

export class LoggerService {
  log(message: string) {
    console.log(`Logger Service: ${message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, suppose I have a UserService that needs the LoggerService to display some messages. We could instantiate the LoggerService directly in UserService like this:

export class UserService {
  private loggerService: LoggerService;

  constructor() {
    this.loggerService = new LoggerService();
  }

  getUser() {
    // Fetch user logic...
    this.loggerService.log('User fetched successfully!');
  }
}
Enter fullscreen mode Exit fullscreen mode

While this approach works, it is generally less flexible and more difficult to maintain. For example, if you ever wanted to replace LoggerService with a different implementation, you would need to manually change all the places in your code where LoggerService is instantiated. Moreover, it's your responsibility to manage the entire lifecycle of that dependency, including its destruction. Hence, we need a mechanism that instances dependencies for us and manages the lifecycle of those dependencies

💥💥And the solution is Dependency Injection (DI):💥💥

Dependency Injection is a design pattern where a class receives its dependencies from an external source rather than creating them itself. These dependencies are often instances of other classes.

Angular implements this pattern for us. The new implementation will look like this:

export class UserService {
  constructor(private loggerService: LoggerService) { }

  getUser() {
    // Fetch user logic...
    this.loggerService.log('User fetched successfully!');
  }
}
Enter fullscreen mode Exit fullscreen mode

There's no need to instantiate the dependency (LoggerService) manually. Angular will do this for us by using the Dependency Injection pattern.

However, if you write this code in your project, unfortunately, you might encounter this type of error: R3InjectorError(AppModule)[LoggerService -> LoggerService -> LoggerService]: NullInjectorError: No provider for LoggerService! 😡😡😡

Don't fear, the solution is simple 😁 😁. The error message "no provider" simply means that the Angular Dependency Injection system can't instantiate a given dependency because it doesn't know how to create it.

In Angular, we define how to create our dependencies using the providers array in the module. By adding our services to this array, we tell Angular when and how to create them. This way, the services are set up and ready for use throughout our application with Angular's Dependency Injection system:

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [{
    provide: LoggerService,
    useClass: LoggerService
  }],
  bootstrap: [AppComponent]
})

Enter fullscreen mode Exit fullscreen mode

In the providers array, we add a new object. To understand this object, we need to ask two questions:

  1. Which class do we want to instantiate and return? This is the value of the useClass property.
  2. When to return this instantiated class? Each time we call LoggerService as a dependency, which is defined by the provide property.

And Angular simplifies this for us; we don't need to write all the mentioned code 😎. We can simply write providers: [LoggerService] and Angular takes care of the rest. And voilà, our service is ready to be injected and used .

Let's stop here so we don't overwhelm you. In our next session, we'll dive deeper into more details and uncover the miracles of Dependency Injection in Angular

Top comments (0)