Load and mutate data in Angular — provideHttpClient setup, GET requests with signals, the httpResource API for reactive reads, and POST mutations with pending state.
Why: HttpClient is Angular's built-in fetch — typed requests, interceptors, and easy testing. Turn it on once with provideHttpClient, inject it, and subscribe to a GET: the callback receives the parsed JSON. Note: .get() returns an Observable — a stream you subscribe to; the request only fires when something subscribes.
// src/app/app.config.ts — turn the HTTP client on once
// providers: [provideHttpClient(withFetch())]
import { HttpClient } from '@angular/common/http'
import { Component, inject, signal } from '@angular/core'
type User = { id: number; name: string }
@Component({
selector: 'app-users',
template: `
<ul>
@for (u of users(); track u.id) {
<li>{{ u.name }}</li>
} @empty {
<li>Loading…</li>
}
</ul>
`,
})
export class Users {
private http = inject(HttpClient)
users = signal<User[]>([])
constructor() {
// .get returns an Observable — subscribe to receive the data
this.http
.get<User[]>('https://jsonplaceholder.typicode.com/users')
.subscribe((data) => this.users.set(data))
}
}Why: httpResource is the signal-era way to fetch — give it a function that builds the URL from signals, and it re-requests automatically whenever those signals change, tracking isLoading and error for you. When: reads that depend on changing state — search, filters, route params. Note: new API — check it is available in your Angular version.
import { httpResource } from '@angular/common/http'
import { Component, signal } from '@angular/core'
type Repo = { full_name: string; stargazers_count: number }
@Component({
selector: 'app-repo-stats',
template: `
@if (repo.isLoading()) {
<p>Loading…</p>
} @else if (repo.error()) {
<p>Something went wrong.</p>
} @else if (repo.hasValue()) {
<p>{{ repo.value().full_name }} — {{ repo.value().stargazers_count }} stars</p>
}
`,
})
export class RepoStats {
name = signal('angular/angular')
// Re-requests automatically whenever name() changes
repo = httpResource<Repo>(() => `https://api.github.com/repos/${this.name()}`)
}Why: writes (POST, PUT, DELETE) need two things reads don't — a pending flag so the user can't double-submit, and a refresh afterwards so the screen matches the server. Subscribe with next/error handlers to cover success and failure.
import { HttpClient } from '@angular/common/http'
import { Component, inject, signal } from '@angular/core'
@Component({
selector: 'app-add-comment',
template: `
<button [disabled]="pending()" (click)="addComment()">
{{ pending() ? 'Sending…' : 'Add comment' }}
</button>
`,
})
export class AddComment {
private http = inject(HttpClient)
pending = signal(false)
addComment() {
this.pending.set(true)
this.http.post('/api/comments', { text: 'Great post!' }).subscribe({
// …re-fetch or update your list here so it matches the server
next: () => this.pending.set(false),
error: () => this.pending.set(false),
})
}
}