npx skills add https://github.com/analogjs/angular-skills --skill angular-httpHow Angular Http fits into a Paperclip company.
Angular Http drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
SKILL.md366 linesExpandCollapse
---name: angular-httpdescription: Implement HTTP data fetching in Angular v20+ using resource(), httpResource(), and HttpClient. Use for API calls, data loading with signals, request/response handling, and interceptors. Triggers on data fetching, API integration, loading states, error handling, or converting Observable-based HTTP to signal-based patterns.--- # 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: ```typescriptimport { 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 ```typescript// Simple GET requestuserResource = httpResource<User>(() => `/api/users/${this.userId()}`); // With full request optionsuserResource = httpResource<User>(() => ({ url: `/api/users/${this.userId()}`, method: 'GET', headers: { 'Authorization': `Bearer ${this.token()}` }, params: { include: 'profile' },})); // With default valueusersResource = httpResource<User[]>(() => '/api/users', { defaultValue: [],}); // Skip request when params undefineduserResource = httpResource<User>(() => { const id = this.userId(); return id ? `/api/users/${id}` : undefined;});``` ### Resource State ```typescript// Status signalsuserResource.value() // Current value or undefineduserResource.hasValue() // Boolean - has resolved valueuserResource.error() // Error or undefineduserResource.isLoading() // Boolean - currently loadinguserResource.status() // 'idle' | 'loading' | 'reloading' | 'resolved' | 'error' | 'local' // ActionsuserResource.reload() // Manually trigger reloaduserResource.set(value) // Set local valueuserResource.update(fn) // Update local value``` ## resource() - Generic Async Data For non-HTTP async operations or custom fetch logic: ```typescriptimport { 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<SearchResult[]>; }, });}``` ### Resource with Default Value ```typescripttodosResource = 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 ```typescriptconst 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: ```typescriptimport { 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 ```typescriptprivate http = inject(HttpClient); // GETgetUser(id: string) { return this.http.get<User>(`/api/users/${id}`);} // POSTcreateUser(user: CreateUserDto) { return this.http.post<User>('/api/users', user);} // PUTupdateUser(id: string, user: UpdateUserDto) { return this.http.put<User>(`/api/users/${id}`, user);} // PATCHpatchUser(id: string, changes: Partial<User>) { return this.http.patch<User>(`/api/users/${id}`, changes);} // DELETEdeleteUser(id: string) { return this.http.delete<void>(`/api/users/${id}`);}``` ### Request Options ```typescriptthis.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) ```typescript// auth.interceptor.tsimport { 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.tsexport 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.tsexport 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 ```typescript// app.config.tsimport { provideHttpClient, withInterceptors } from '@angular/common/http'; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient( withInterceptors([ authInterceptor, errorInterceptor, loggingInterceptor, ]) ), ],};``` ## Error Handling ### With httpResource ```typescript@Component({ template: ` @if (userResource.error(); as error) { <div class="error"> <p>{{ getErrorMessage(error) }}</p> <button (click)="userResource.reload()">Retry</button> </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 ```typescriptimport { 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 ```typescript@Component({ template: ` @switch (dataResource.status()) { @case ('idle') { <p>Enter a search term</p> } @case ('loading') { <app-spinner /> } @case ('reloading') { <app-data [data]="dataResource.value()" /> <app-spinner size="small" /> } @case ('resolved') { <app-data [data]="dataResource.value()" /> } @case ('error') { <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](references/http-patterns.md).Angular Component
The angular-component skill generates modern Angular v20+ standalone components using signal-based inputs and outputs, OnPush change detection, and host binding
Angular Di
Covers Angular v20+ dependency injection patterns with modern inject() syntax instead of constructor injection. Shows provider configuration at root, component,
Angular Directives
Solid reference for building Angular directives the modern way with v20+ patterns. Covers attribute directives using the new `input()` function and `host` prope