Skip to content
beginner Phase 5 · Services and Dependency Injection

Providing and Injecting Services

Register a service once and resolve it wherever it is needed.

35m
0 problems
Topic Progress 0%

Creating and Injecting Services

Services are the backbone of Angular applications. They encapsulate business logic, data access, and shared state. Angular's dependency injection system creates and delivers service instances.

Creating a Service

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

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private users: User[] = [];

  getUsers(): User[] {
    return this.users;
  }

  addUser(user: User): void {
    this.users.push(user);
  }

  removeUser(id: number): void {
    this.users = this.users.filter(u => u.id !== id);
  }
}

Injecting a Service

import { Component, inject } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-list',
  template: `<p *ngFor="let user of users">{{ user.name }}</p>`,
})
export class UserListComponent {
  private userService = inject(UserService);
  users = this.userService.getUsers();
}

Constructor Injection (Traditional)

@Component({
  selector: 'app-user-list',
  template: `<p *ngFor="let user of users">{{ user.name }}</p>`,
})
export class UserListComponent {
  users: User[];

  constructor(private userService: UserService) {
    this.users = this.userService.getUsers();
  }
}

providedIn: 'root'

The providedIn: 'root' option registers the service as a singleton at the application root level:

  • One instance shared across the entire application
  • Lazy-loaded — only created when first injected
  • Automatically tree-shakable if never used

Alternative Provider Scopes

// Application-wide singleton (default)
@Injectable({ providedIn: 'root' })

// Scoped to a specific NgModule
@Injectable({ providedIn: SomeModule })

// Scoped to a specific component
@Component({
  providers: [UserService],
})

Functional Injection with inject()

Angular's inject() function provides a cleaner way to inject dependencies outside of constructor parameter lists.

Why Use inject()?

// Constructor injection — verbose with many dependencies
constructor(
  private userService: UserService,
  private authService: AuthService,
  private logger: LoggerService,
  private config: ConfigService,
) {}

// With inject() — cleaner
private userService = inject(UserService);
private authService = inject(AuthService);
private logger = inject(LoggerService);
private config = inject(ConfigService);

inject() in Functions

import { inject } from '@angular/core';
import { Router } from '@angular/router';

export function authGuard(): boolean {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (!authService.isLoggedIn) {
    router.navigate(['/login']);
    return false;
  }
  return true;
}

inject() with Optional Dependencies

import { inject, Optional } from '@angular/core';

// Returns null if the service is not provided
private analytics = inject(AnalyticsService, { optional: true });

inject() with InjectionToken

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

export const API_URL = new InjectionToken<string>('API_URL');

// In a component or service
private apiUrl = inject(API_URL);

When to Use Which

Approach Best For
Constructor injection Traditional class-based components/services
inject() Functional guards, resolvers, utility functions
inject() with options Optional dependencies, skip-self injection

Quiz

1. What does providedIn: 'root' do?

Question 1 options

2. What is the inject() function used for?

Question 2 options

3. How do you inject an optional dependency?

Question 3 options

Flashcards

Question

How do you create an Angular service?

Answer

@Injectable({ providedIn: 'root' }) on a class. The 'root' scope makes it an app-wide singleton.

Question

What does inject() do?

Answer

Resolves dependencies from the injector outside constructor parameters. Useful in functional guards and resolvers.

Question

What is providedIn: 'root'?

Answer

Registers the service as an application-wide singleton. Lazy-loaded and tree-shakable.

Revision Notes

Key Takeaways

  • 1. @Injectable({ providedIn: 'root' }) creates an app-wide singleton
  • 2. inject() resolves dependencies outside constructors
  • 3. Services are lazy-loaded and tree-shakable
  • 4. Use inject() with { optional: true } for optional dependencies

Interview Tips

  • • Explain the benefits of providedIn: 'root' (tree-shakable, lazy-loaded)
  • • Be ready to compare constructor injection vs inject()
  • • Mention that services should be provided at the root level by default

Cheat Sheet

Providing and Injecting Services Cheat Sheet

Create service:

@Injectable({ providedIn: 'root' })
export class UserService {}

Inject in component:

// Constructor
constructor(private userService: UserService) {}

// Or with inject()
private userService = inject(UserService);

Optional injection:

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

Continue Learning