← Back to all tutorials

Services

Create Angular services — shared logic, dependency injection, and separating data logic from components.

Services

Services are classes that handle shared logic and data — fetching data, business rules, logging, or anything that doesn't belong in a component. Angular's dependency injection system delivers services to any component that needs them.

Why Services?

  • Components should only handle view logic (displaying data, handling events)
  • Data fetching, business logic, and shared state belong in services
  • Services are reusable across multiple components
  • Services are easier to test independently

Generate a Service

ng generate service services/user
# or
ng g s services/user

Basic Service

// user.service.ts
import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'  // Available app-wide (singleton)
})
export class UserService {
    private users = [
        { id: 1, name: 'Alice', role: 'Admin' },
        { id: 2, name: 'Bob', role: 'Developer' },
        { id: 3, name: 'Carol', role: 'Designer' },
    ];

    getUsers() {
        return this.users;
    }

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

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

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

Injecting a Service

// user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';

@Component({
    selector: 'app-user-list',
    template: `
        <h2>Users</h2>
        <div *ngFor="let user of users">
            <h3>{{ user.name }}</h3>
            <p>{{ user.role }}</p>
            <button (click)="remove(user.id)">Delete</button>
        </div>
    `
})
export class UserListComponent implements OnInit {
    users: any[] = [];

    // Inject the service via constructor
    constructor(private userService: UserService) {}

    ngOnInit(): void {
        this.users = this.userService.getUsers();
    }

    remove(id: number): void {
        this.userService.deleteUser(id);
        this.users = this.userService.getUsers();
    }
}

How Dependency Injection Works

// 1. Angular sees the constructor parameter type: UserService
// 2. It looks up UserService in its injector registry
// 3. It creates (or reuses) an instance
// 4. It passes the instance to the constructor

constructor(private userService: UserService) {}
// 'private' automatically creates this.userService

providedIn: 'root'

@Injectable({
    providedIn: 'root'  // Singleton — one instance shared across the ENTIRE app
})

With providedIn: 'root', you don't need to add the service to any module's providers array — Angular handles it automatically.

Sharing Data Between Components

// The same service instance is injected into both components.
// Changes in one are visible in the other!

// Component A
constructor(private userService: UserService) {}
addUser() { this.userService.addUser({ name: 'Dave' }); }

// Component B
constructor(private userService: UserService) {}
ngOnInit() { this.users = this.userService.getUsers(); }
// Dave appears here after Component A adds them!

Service with Logging

@Injectable({ providedIn: 'root' })
export class LogService {
    log(message: string): void {
        const timestamp = new Date().toISOString();
        console.log(`[${timestamp}] ${message}`);
    }

    error(message: string): void {
        console.error(`[ERROR] ${message}`);
    }
}

// Inject in any component:
constructor(private logger: LogService) {}

ngOnInit() {
    this.logger.log('Component initialized');
}

Key Takeaways

  • Services handle shared logic — data, business rules, API calls
  • @Injectable({ providedIn: 'root' }) makes it a singleton available app-wide
  • Inject services via the constructor: constructor(private svc: MyService)
  • Multiple components share the same service instance — great for shared state
  • Keep components thin — move logic to services