Skip to content

@Injectable and the DI System

Angular uses dependency injection (DI) to supply services to the components and other services that need them. Instead of components creating their own service instances with new, Angular creates services and injects them automatically.

A service is a plain TypeScript class marked with the @Injectable decorator:

import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MovieService {
// methods here
}

The @Injectable decorator has one important option: providedIn. Setting it to 'root' tells Angular to create one instance of this service and make it available throughout the entire application. This is called a root-level singleton.

When a component declares a dependency:

export class Home {
constructor(private movieService: MovieService) {}
}

Angular’s DI system:

  1. Looks at the constructor parameter type: MovieService
  2. Finds the service registered with providedIn: 'root'
  3. Returns the existing singleton instance (or creates it if this is the first request)
  4. Assigns it to this.movieService

You never call new MovieService(). Angular manages the lifecycle.

Testing: In tests, you can provide a mock service instead of the real one. The component under test receives the mock automatically.

Singleton state: Every component that injects WatchlistService gets the same instance. When one component modifies the watchlist, all others see the updated state immediately.

Decoupling: Components do not know how services are constructed. If MovieService starts needing HttpClient, you add that dependency to MovieService — the components that use it are unaffected.

Services can depend on other services the same way components do:

@Injectable({ providedIn: 'root' })
export class MovieService {
constructor(private http: HttpClient) {}
getPopular(): Observable<MoviesResponse> {
return this.http.get<MoviesResponse>(`${BASE_URL}/movie/popular`, {
params: { api_key: API_KEY }
});
}
}

HttpClient is Angular’s built-in HTTP service. MovieService declares it as a constructor dependency, and Angular injects it automatically — the same way components inject MovieService.

Angular maintains an injector tree — a hierarchy of containers that hold service instances. The root injector holds root-level services. Feature-level injectors can hold services scoped to a specific part of the app.

For this course, providedIn: 'root' is sufficient for all services. You do not need to configure the injector tree manually.

  1. Generate a service: ng generate service services/logger.
  2. Open the generated file. Notice @Injectable({ providedIn: 'root' }).
  3. Add a log(message: string): void { console.log('[Logger]', message); } method.
  4. Inject LoggerService into AppComponent’s constructor and call this.logger.log('App initialized') in ngOnInit.
  5. Run the app and confirm the log message appears in the browser console.
  • @Injectable({ providedIn: 'root' }) registers a service as a root-level singleton — one instance for the whole app.
  • Angular’s DI system reads constructor parameter types and injects the matching service instances automatically.
  • Services can inject other services the same way components do.
  • Root-level singletons share state across all components that inject them.
  • The providedIn: 'root' pattern is the right default for all services in this course.