Angular services hold shared logic and data. Dependency Injection delivers them to any component that asks. This is how Angular apps share state and communicate with APIs.
Components should handle display. Services handle logic, data, and communication. A component that fetches data directly is harder to test and harder to reuse. Extract that work into a service.
ng generate service task
# creates: task.service.tsimport { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root' // available app-wide, no need to register
})
export class TaskService {
private apiUrl = 'https://jsonplaceholder.typicode.com/todos';
constructor(private http: HttpClient) {}
getTasks(): Observable {
return this.http.get(this.apiUrl);
}
deleteTask(id: number): Observable {
return this.http.delete(`${this.apiUrl}/${id}`);
}
} app.module.ts imports, or Angular can't inject HttpClient into your service.import { Component, OnInit } from '@angular/core';
import { TaskService } from '../task.service';
@Component({
selector: 'app-task-list',
templateUrl: './task-list.component.html'
})
export class TaskListComponent implements OnInit {
tasks: any[] = [];
loading = true;
error = '';
// Angular injects TaskService automatically via DI
constructor(private taskService: TaskService) {}
ngOnInit(): void {
this.taskService.getTasks().subscribe({
next: (data) => { this.tasks = data.slice(0, 20); this.loading = false; },
error: (err) => { this.error = 'Failed to load'; this.loading = false; }
});
}
}Loading...
{{ error }}
-
{{ task.title }}
✓
Instead of subscribing manually in ngOnInit, you can pass an Observable directly to the template and use the async pipe. It subscribes, unwraps, and unsubscribes automatically.
tasks$ = this.taskService.getTasks(); // Observable, not subscribed yet{{ task.title }} user service that fetches from https://jsonplaceholder.typicode.com/users.user-list component that injects the service.*ngFor, show a loading state, and handle errors.async pipe version as a refactor after the manual subscribe works.@Injectable({ providedIn: 'root' }) makes a service available everywhere — no module registration needed.async pipe subscribes to Observables and unsubscribes on destroy — prefer it over manual subscriptions.