Load Articles with Angular HttpClient: Loading and Errors
Represent loading, success, empty results, and errors when fetching articles from an API.
You are reading a translated version.
An empty list does not necessarily mean there are no articles. A request might still be running or might have failed. The interface should distinguish those states.
Register provideHttpClient() in your application providers. This component expects GET /api/articles to return an array of objects with id and title. Supply that endpoint yourself; if your API returns a pagination object, adjust the response mapping.
Implementation example
import { AsyncPipe } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, inject } from '@angular/core';
import { catchError, map, of, startWith } from 'rxjs';
type Article = { id: number; title: string };
@Component({
selector: 'app-article-list',
standalone: true,
imports: [AsyncPipe],
template: `
@if (state$ | async; as state) {
@if (state.loading) { <p>Loading…</p> }
@else if (state.error) { <p role="alert">{{ state.error }}</p> }
@else {
@for (article of state.articles; track article.id) {
<h2>{{ article.title }}</h2>
} @empty { <p>No articles yet.</p> }
}
}
`,
})
export class ArticleList {
private http = inject(HttpClient);
state$ = this.http.get<Article[]>('/api/articles').pipe(
map(articles => ({ loading: false, articles, error: '' })),
startWith({ loading: true, articles: [] as Article[], error: '' }),
catchError(() => of({
loading: false, articles: [] as Article[], error: 'Unable to load articles.',
})),
);
}
Check the result
Use network throttling to observe loading, an empty array response for the empty state, and HTTP 500 for the error state. Do not expose server exception details to readers.
A single AsyncPipe in the outer block avoids multiple HTTP subscriptions for the same view. The Article[] type helps TypeScript but does not validate JSON at runtime. An API on a different origin also requires appropriate server-side CORS configuration.
