Components should own only UI logic. Everything else — fetching data, business rules, shared state — belongs in a service. Angular's dependency injection (DI) system instantiates services for you and provides them wherever they're needed.
Creating a Service
ng generate service user// src/app/user.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // singleton across the whole app
})
export class UserService {
private users = [
{ id: 1, name: 'Sabaoon', role: 'Developer' },
{ id: 2, name: 'Aisha', role: 'Designer' },
{ id: 3, name: 'Omar', role: 'QA' },
]
getAll() {
return this.users
}
getById(id: number) {
return this.users.find(u => u.id === id)
}
}providedIn: 'root' means Angular creates one instance for the entire app and tree-shakes the service if nothing uses it.
Injecting into a Component
Add the service as a constructor parameter — Angular resolves it automatically:
import { Component, OnInit } from '@angular/core';
import { UserService } from '../user.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
users: any[] = []
constructor(private userService: UserService) {}
ngOnInit() {
this.users = this.userService.getAll()
}
}ngOnInit is the right lifecycle hook for fetching initial data — it runs after Angular sets all inputs.
HTTP Requests
Real services fetch data over HTTP. Angular ships HttpClient for this:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class PostService {
private api = 'https://jsonplaceholder.typicode.com'
constructor(private http: HttpClient) {}
getPosts(): Observable<any[]> {
return this.http.get<any[]>(`${this.api}/posts`)
}
}
Subscribe in the component:
ngOnInit() {
this.postService.getPosts().subscribe({
next: posts => this.posts = posts,
error: err => console.error(err),
})
}Remember to add HttpClientModule to your AppModule imports.
What a Service-Driven List Looks Like
Lifecycle Hooks
| Hook | When it runs |
|---|---|
ngOnInit | Once, after first render — best for data fetching |
ngOnChanges | Every time an @Input changes |
ngOnDestroy | Just before the component is removed — clean up subscriptions |
ngAfterViewInit | After the component's view and child views are initialised |
Always unsubscribe from Observables in ngOnDestroy to prevent memory leaks, or use the async pipe which unsubscribes automatically.