angular-http

Angular HTTP & Data Fetching

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "angular-http" with this command: npx skills add zard-ui/zardui/zard-ui-zardui-angular-http

Angular HTTP & Data Fetching

Fetch data in Angular using signal-based resource() , httpResource() , and the traditional HttpClient .

httpResource() - Signal-Based HTTP

httpResource() wraps HttpClient with signal-based state management:

import { Component, signal } from '@angular/core'; import { httpResource } from '@angular/common/http';

interface User { id: number; name: string; email: string; }

@Component({ selector: 'app-user-profile', template: @if (userResource.isLoading()) { <p>Loading...</p> } @else if (userResource.error()) { <p>Error: {{ userResource.error()?.message }}</p> <button (click)="userResource.reload()">Retry</button> } @else if (userResource.hasValue()) { <h1>{{ userResource.value().name }}</h1> <p>{{ userResource.value().email }}</p> } , }) export class UserProfile { userId = signal('123');

// Reactive HTTP resource - refetches when userId changes userResource = httpResource<User>(() => /api/users/${this.userId()}); }

httpResource Options

// Simple GET request userResource = httpResource<User>(() => /api/users/${this.userId()});

// With full request options userResource = httpResource<User>(() => ({ url: /api/users/${this.userId()}, method: 'GET', headers: { 'Authorization': Bearer ${this.token()} }, params: { include: 'profile' }, }));

// With default value usersResource = httpResource<User[]>(() => '/api/users', { defaultValue: [], });

// Skip request when params undefined userResource = httpResource<User>(() => { const id = this.userId(); return id ? /api/users/${id} : undefined; });

Resource State

// Status signals userResource.value() // Current value or undefined userResource.hasValue() // Boolean - has resolved value userResource.error() // Error or undefined userResource.isLoading() // Boolean - currently loading userResource.status() // 'idle' | 'loading' | 'reloading' | 'resolved' | 'error' | 'local'

// Actions userResource.reload() // Manually trigger reload userResource.set(value) // Set local value userResource.update(fn) // Update local value

resource() - Generic Async Data

For non-HTTP async operations or custom fetch logic:

import { resource, signal } from '@angular/core';

@Component({...}) export class Search { query = signal('');

searchResource = resource({ // Reactive params - triggers reload when changed params: () => ({ q: this.query() }),

// Async loader function
loader: async ({ params, abortSignal }) => {
  if (!params.q) return [];
  
  const response = await fetch(`/api/search?q=${params.q}`, {
    signal: abortSignal,
  });
  return response.json() as Promise&#x3C;SearchResult[]>;
},

}); }

Resource with Default Value

todosResource = resource({ defaultValue: [] as Todo[], params: () => ({ filter: this.filter() }), loader: async ({ params }) => { const res = await fetch(/api/todos?filter=${params.filter}); return res.json(); }, });

// value() returns Todo[] (never undefined)

Conditional Loading

const userId = signal<string | null>(null);

userResource = resource({ params: () => { const id = userId(); // Return undefined to skip loading return id ? { id } : undefined; }, loader: async ({ params }) => { return fetch(/api/users/${params.id}).then(r => r.json()); }, }); // Status is 'idle' when params returns undefined

HttpClient - Traditional Approach

For complex scenarios or when you need Observable operators:

import { Component, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { toSignal } from '@angular/core/rxjs-interop';

@Component({...}) export class Users { private http = inject(HttpClient);

// Convert Observable to Signal users = toSignal( this.http.get<User[]>('/api/users'), { initialValue: [] } );

// Or use Observable directly users$ = this.http.get<User[]>('/api/users'); }

HTTP Methods

private http = inject(HttpClient);

// GET getUser(id: string) { return this.http.get<User>(/api/users/${id}); }

// POST createUser(user: CreateUserDto) { return this.http.post<User>('/api/users', user); }

// PUT updateUser(id: string, user: UpdateUserDto) { return this.http.put<User>(/api/users/${id}, user); }

// PATCH patchUser(id: string, changes: Partial<User>) { return this.http.patch<User>(/api/users/${id}, changes); }

// DELETE deleteUser(id: string) { return this.http.delete<void>(/api/users/${id}); }

Request Options

this.http.get<User[]>('/api/users', { headers: { 'Authorization': 'Bearer token', 'Content-Type': 'application/json', }, params: { page: '1', limit: '10', sort: 'name', }, observe: 'response', // Get full HttpResponse responseType: 'json', });

Interceptors

Functional Interceptor (Recommended)

// auth.interceptor.ts import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core';

export const authInterceptor: HttpInterceptorFn = (req, next) => { const authService = inject(Auth); const token = authService.token();

if (token) { req = req.clone({ setHeaders: { Authorization: Bearer ${token} }, }); }

return next(req); };

// error.interceptor.ts export const errorInterceptor: HttpInterceptorFn = (req, next) => { return next(req).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401) { inject(Router).navigate(['/login']); } return throwError(() => error); }) ); };

// logging.interceptor.ts export const loggingInterceptor: HttpInterceptorFn = (req, next) => { const started = Date.now(); return next(req).pipe( tap({ next: () => console.log(${req.method} ${req.url} - ${Date.now() - started}ms), error: (err) => console.error(${req.method} ${req.url} failed, err), }) ); };

Register Interceptors

// app.config.ts import { provideHttpClient, withInterceptors } from '@angular/common/http';

export const appConfig: ApplicationConfig = { providers: [ provideHttpClient( withInterceptors([ authInterceptor, errorInterceptor, loggingInterceptor, ]) ), ], };

Error Handling

With httpResource

@Component({ template: @if (userResource.error(); as error) { &#x3C;div class="error"> &#x3C;p>{{ getErrorMessage(error) }}&#x3C;/p> &#x3C;button (click)="userResource.reload()">Retry&#x3C;/button> &#x3C;/div> } , }) export class UserCmpt { userResource = httpResource<User>(() => /api/users/${this.userId()});

getErrorMessage(error: unknown): string { if (error instanceof HttpErrorResponse) { return error.error?.message || Error ${error.status}: ${error.statusText}; } return 'An unexpected error occurred'; } }

With HttpClient

import { catchError, retry } from 'rxjs';

getUser(id: string) { return this.http.get<User>(/api/users/${id}).pipe( retry(2), // Retry up to 2 times catchError((error: HttpErrorResponse) => { console.error('Error fetching user:', error); return throwError(() => new Error('Failed to load user')); }) ); }

Loading States Pattern

@Component({ template: @switch (dataResource.status()) { @case ('idle') { &#x3C;p>Enter a search term&#x3C;/p> } @case ('loading') { &#x3C;app-spinner /> } @case ('reloading') { &#x3C;app-data [data]="dataResource.value()" /> &#x3C;app-spinner size="small" /> } @case ('resolved') { &#x3C;app-data [data]="dataResource.value()" /> } @case ('error') { &#x3C;app-error [error]="dataResource.error()" (retry)="dataResource.reload()" /> } } , }) export class Data { query = signal(''); dataResource = httpResource<Data[]>(() => this.query() ? /api/search?q=${this.query()} : undefined ); }

For advanced patterns, see references/http-patterns.md.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

angular-signals

No summary provided by upstream source.

Repository SourceNeeds Review
General

angular-di

No summary provided by upstream source.

Repository SourceNeeds Review
General

angular-testing

No summary provided by upstream source.

Repository SourceNeeds Review