Services hold reusable business logic, API calls, and shared state. Dependency injection (DI) lets Angular create and deliver those services wherever they are needed.

Creating a Service

  ng generate service services/user
  
  import { Injectable } from '@angular/core';

export interface User {
  id: number;
  name: string;
  email: string;
}

@Injectable({
  providedIn: 'root'  // Singleton available app-wide
})
export class UserService {
  private users: User[] = [
    { id: 1, name: 'Alice', email: '[email protected]' },
    { id: 2, name: 'Bob', email: '[email protected]' }
  ];

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

  getUserById(id: number): User | undefined {
    return this.users.find(u => u.id === id);
  }

  addUser(user: Omit<User, 'id'>): User {
    const newUser = { ...user, id: Date.now() };
    this.users.push(newUser);
    return newUser;
  }
}
  

providedIn: 'root' registers the service with the root injector — one instance for the entire app.

Injecting a Service

Use the inject() function or constructor injection:

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

@Component({
  selector: 'app-user-list',
  standalone: true,
  template: `
    @for (user of users; track user.id) {
      <p>{{ user.name }} — {{ user.email }}</p>
    }
  `
})
export class UserListComponent {
  private userService = inject(UserService);
  users = this.userService.getUsers();
}
  

Sharing State Between Components

Services act as a lightweight store when multiple components need the same data:

  @Injectable({ providedIn: 'root' })
export class CartService {
  private items: string[] = [];

  addItem(item: string) {
    this.items.push(item);
  }

  getItems(): string[] {
    return [...this.items];
  }

  get count(): number {
    return this.items.length;
  }
}
  
  @Component({
  selector: 'app-cart-badge',
  standalone: true,
  template: `<span>Cart ({{ cart.count }})</span>`
})
export class CartBadgeComponent {
  cart = inject(CartService);
}
  

Provider Scopes

Registration Scope
providedIn: 'root' App-wide singleton
Component providers: [MyService] New instance per component
Route providers: [MyService] New instance per route

Component-scoped example:

  @Component({
  selector: 'app-editor',
  standalone: true,
  providers: [EditorStateService],
  template: `<textarea></textarea>`
})
export class EditorComponent {}
  

Each app-editor instance gets its own EditorStateService.

HTTP Services

Services commonly wrap HttpClient (covered in the HTTP chapter):

  @Injectable({ providedIn: 'root' })
export class ApiService {
  private http = inject(HttpClient);
  private baseUrl = '/api';

  getPosts() {
    return this.http.get<Post[]>(`${this.baseUrl}/posts`);
  }
}
  

Use services to keep components thin — components handle presentation; services handle data and logic.