DEV Community

Atilla Baspinar
Atilla Baspinar

Posted on

Dependency Injection

Dependency Injection (DI) is a pattern where dependencies are supplied to a class instead of created inside it. Angular has a built-in DI system that handles creation and sharing of services.


1. Creating a Service

Decorate a class with @Injectable to make it injectable. providedIn: 'root' registers it as an app-wide singleton that is also tree-shakable — if nothing injects it, it won't be in the bundle:

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

@Injectable({ providedIn: 'root' })
export class AuthService {
  isLoggedIn = false;

  login() { this.isLoggedIn = true; }
  logout() { this.isLoggedIn = false; }
}
Enter fullscreen mode Exit fullscreen mode

2. @Service() — Angular 22+

Angular 22 introduced @Service() as a shorthand for @Injectable({ providedIn: 'root' }). Less boilerplate, same behavior:

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

@Service()
export class AuthService {
  isLoggedIn = false;

  login() { this.isLoggedIn = true; }
  logout() { this.isLoggedIn = false; }
}
Enter fullscreen mode Exit fullscreen mode

Note: @Service() only works with inject() — it does not support constructor injection. For most new services, prefer @Service() when targeting Angular 22+.


3. Injecting a Service

Use the inject() function in a class field — this is the modern, preferred approach:

import { Component, inject } from '@angular/core';
import { AuthService } from './auth.service';

@Component({ ... })
export class NavbarComponent {
  private auth = inject(AuthService);

  logout() { this.auth.logout(); }
}
Enter fullscreen mode Exit fullscreen mode

Constructor injection is equivalent and still valid, but inject() is cleaner:

constructor(private auth: AuthService) {}
Enter fullscreen mode Exit fullscreen mode

4. Providing at Component Level

Adding a service to a component's providers array creates a new instance scoped to that component and its children. The root singleton is untouched.

@Component({
  selector: 'app-cart',
  providers: [CartService],
  template: `...`,
})
export class CartComponent {
  private cart = inject(CartService);
}
Enter fullscreen mode Exit fullscreen mode

Useful when different parts of the UI need isolated state (e.g. two independent forms on the same page).


5. InjectionToken

For non-class values like config objects, strings, or primitives, use InjectionToken. TypeScript interfaces are erased at runtime and cannot be used as tokens.

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

export interface AppConfig {
  apiUrl: string;
  production: boolean;
}

export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
Enter fullscreen mode Exit fullscreen mode

Provide it where you bootstrap the app:

bootstrapApplication(AppComponent, {
  providers: [
    { provide: APP_CONFIG, useValue: { apiUrl: 'https://api.example.com', production: false } }
  ]
});
Enter fullscreen mode Exit fullscreen mode

Inject it normally:

private config = inject(APP_CONFIG);
Enter fullscreen mode Exit fullscreen mode

6. Provider Types

useValue — static value

The most common use is injecting configuration:

providers: [
  { provide: APP_CONFIG, useValue: { apiUrl: '...', production: false } }
]
Enter fullscreen mode Exit fullscreen mode

useClass — substitute an implementation

Swap one class for another — handy for mocking in tests:

providers: [
  { provide: Logger, useClass: MockLogger }
]
Enter fullscreen mode Exit fullscreen mode

useFactory — compute the value at runtime

When the value depends on other services or runtime conditions:

providers: [
  {
    provide: LoggerService,
    useFactory: () => {
      const config = inject(APP_CONFIG);
      return new LoggerService(config.production ? 'error' : 'debug');
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

7. Optional Injection

If a dependency may or may not be provided, use { optional: true } to get null instead of an error:

private analytics = inject(AnalyticsService, { optional: true });

ngOnInit() {
  this.analytics?.track('page_view');
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)