Day 2 of 5
⏱ ~60 minutes
Angular in 5 Days — Day 2

Services and Dependency Injection

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.

Why Services Exist

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.

Terminal
ng generate service task
# creates: task.service.ts
task.service.ts
import { 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}`);
  }
}
💡
Register HttpClientModule in app.module.ts imports, or Angular can't inject HttpClient into your service.

Injecting a Service into a Component

task-list.component.ts
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; }
    });
  }
}
task-list.component.html
Loading...

{{ error }}

  • {{ task.title }}

The async Pipe

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.

Component (simplified)
tasks$ = this.taskService.getTasks();  // Observable, not subscribed yet
Template
  • {{ task.title }}
  • 📝 Day 2 Exercise
    Build a User Directory
    1. Generate a user service that fetches from https://jsonplaceholder.typicode.com/users.
    2. Generate a user-list component that injects the service.
    3. Display users using *ngFor, show a loading state, and handle errors.
    4. Add a search input. Filter the displayed users in the component class (not in the service).
    5. Use the async pipe version as a refactor after the manual subscribe works.

    Day 2 Summary

    • Services hold logic. Components handle display. Keep them separate.
    • @Injectable({ providedIn: 'root' }) makes a service available everywhere — no module registration needed.
    • Constructor injection: Angular reads the type annotation and delivers the right service instance.
    • The async pipe subscribes to Observables and unsubscribes on destroy — prefer it over manual subscriptions.
    Finished this lesson?