The Heart of Reusability: @Injectable and Dependency Injection

Discover how Angular's core DI system allows you to build modular, efficient, and easily testable applications.

tangled_wires

Welcome! Let's explore how Angular services create clean, reusable code.

/* Two components, duplicated logic... */

What is a Service?

In Angular, a service is a class with a narrow, well-defined purpose. It should do something specific and do it well. For example, a service could be responsible for fetching data from a server, logging user actions, or sharing data between components. This separates concerns and makes your code cleaner and easier to manage.

Creating a Service with @Injectable

To tell Angular that a class can be used as a service, you mark it with the @Injectable() decorator. The option providedIn: 'root' is crucial—it tells Angular to create a single, shared instance of this service for the entire application. This is known as a singleton pattern.

Dependency Injection in Action

Once a service is created, components (or other services) can receive it through Dependency Injection. You simply request it in the component's constructor by specifying its type. Angular's injector handles the rest, "injecting" the shared service instance for you. You don't need to create it with `new`.

The Benefit: A Single Instance

Because providedIn: 'root' creates a single instance, any data or state held within that service is shared. If one component updates the data in the service, another component that injects the same service will instantly see that updated data. This is a powerful way to manage application state.

Practice Zone


Interactive Test 1: Match the Concepts

Drag the Angular concepts to their correct descriptions.

Arrastra en el orden correspondiente.


Arrastra las opciones:

@Injectable()
constructor(private myService: ...)

Completa el código:

Marks a class as a service______
Requests the service in a component______
Unlock with Premium

Interactive Test 2: Complete the Service

Rellena los huecos en cada casilla.

import { Injectable } from '@angular/core';

({ 
  providedIn: 'root' 
})
export class LoggerService {
  log(message: string) {
    console.log(message);
  }
}
Unlock with Premium

Practice Example: Code Editor

Create a service named `DataService` with a method `getData()` that returns a string. Then, create a component that injects this service and displays the string in its template.

* Write the code below. Correct characters will be shown in green and incorrect ones in red.

/* data.service.ts */ import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DataService { getData() { return 'Data from the service!'; } } /* app.component.ts */ import { Component } from '@angular/core'; import { DataService } from './data.service'; @Component({ ... }) export class AppComponent { myData: string; constructor(private dataService: DataService) { this.myData = this.dataService.getData(); } }
Unlock with Premium

Knowledge Check

What is the main purpose of `providedIn: 'root'` in an @Injectable() decorator?


Unlock with Premium

Services in a Real Application

Services are the workhorses of an Angular application. They are the ideal place to contain business logic, freeing your components to focus only on the user interface.


1. Communicating with Servers

One of the most common uses for a service is to handle HTTP communication. By encapsulating API calls in a service, you can easily reuse that logic in any component that needs data, without duplicating code.

@Injectable({ providedIn: 'root' })
export class ApiService {
  constructor(private http: HttpClient) {}

  getUsers() {
    return this.http.get('/api/users');
  }
}

2. Managing Shared State

When multiple components need to react to the same data, a service acts as the "single source of truth." Components can subscribe to data streams (like RxJS Subjects) within the service to get real-time updates.

// In CartService
private cart = new BehaviorSubject<Item[]>([]);
public cart$ = this.cart.asObservable();

// In any Component
this.cartService.cart$.subscribe(items => ...);

3. Simplifying Unit Tests

Dependency Injection makes your code incredibly easy to test. When testing a component, you can provide a "mock" or "fake" version of its service dependencies. This allows you to test the component in isolation, without making real HTTP requests or relying on a complex state.

TestBed.configureTestingModule({
  declarations: [ UserProfileComponent ],
  providers: [
    { provide: UserService, useClass: MockUserService }
  ]
});

Practical Takeaway: Master services and Dependency Injection, and you've mastered the foundation of scalable, maintainable, and testable Angular architecture.

Angular DI Glossary

@Injectable()
A decorator that marks a class as available to be provided and injected as a dependency. It is the fundamental building block for Angular services.
Dependency Injection (DI)
A design pattern and mechanism for creating and delivering parts of an application (dependencies) to other parts that need them. In Angular, you request dependencies in a class constructor.
Service
A broad category for any class with a focused purpose that you create to be shared across components, such as data fetching, logging, or calculations.
Injector
An object in the Angular DI framework that is responsible for finding a named dependency in its internal provider list, creating an instance if needed, and delivering it.
Provider
An instruction, or "recipe," that tells the Injector *how* to create an instance of a dependency. The providedIn: 'root' syntax is a shortcut for creating a provider.
providedIn: 'root'
A property within @Injectable() that automatically registers the service provider with the application's root injector. This makes the service a singleton available everywhere.