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?
2. What is the inject() function used for?
3. How do you inject an optional dependency?
Flashcards
Question
How do you create an Angular service?
Click to reveal answer
Answer
@Injectable({ providedIn: 'root' }) on a class. The 'root' scope makes it an app-wide singleton.
Question
What does inject() do?
Click to reveal answer
Answer
Resolves dependencies from the injector outside constructor parameters. Useful in functional guards and resolvers.
Question
What is providedIn: 'root'?
Click to reveal answer
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 });