Episode 23 of 27
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