skills/angular/SKILL.md
Use when editing Angular projects, angular.json, *.component.ts files, @Component code, signals, standalone components, control-flow blocks, Angular migrations, or Angular version-specific APIs.
npx skillsauth add cofin/flow angularInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
import { Component, signal, computed, effect, input, output } from '@angular/core';
@Component({
selector: 'app-item-list',
standalone: true,
imports: [],
template: `
<h2>{{ title() }} ({{ count() }})</h2>
<ul>
@for (item of items(); track item.id) {
<li (click)="selectItem(item)">{{ item.name }}</li>
}
</ul>
`
})
export class ItemListComponent {
// Input signals
title = input.required<string>();
items = input.required<Item[]>();
// Output
itemSelected = output<Item>();
// Local state
selected = signal<Item | null>(null);
// Computed
count = computed(() => this.items().length);
constructor() {
effect(() => {
console.log('Selected:', this.selected());
});
}
selectItem(item: Item) {
this.selected.set(item);
this.itemSelected.emit(item);
}
}
</example>
<!-- @if -->
@if (loading()) {
<app-spinner />
} @else if (error()) {
<app-error [message]="error()!" />
} @else {
<app-content [data]="data()" />
}
<!-- @for -->
@for (item of items(); track item.id; let i = $index, first = $first) {
<div [class.first]="first">{{ i + 1 }}. {{ item.name }}</div>
} @empty {
<p>No items found</p>
}
<!-- @switch -->
@switch (status()) {
@case ('loading') { <app-spinner /> }
@case ('error') { <app-error /> }
@default { <app-content /> }
}
<!-- @defer -->
@defer (on viewport) {
<app-heavy-component />
} @loading (minimum 200ms) {
<app-skeleton />
}
</example>
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { toSignal } from '@angular/core/rxjs-interop';
@Injectable({ providedIn: 'root' })
export class ItemService {
private http = inject(HttpClient);
items = toSignal(this.http.get<Item[]>('/api/items'), {
initialValue: []
});
async create(item: CreateItemDto): Promise<Item> {
return await firstValueFrom(
this.http.post<Item>('/api/items', item)
);
}
}
</example>
inject() instead of constructor injection -- More concise, better type inference, and works seamlessly with functional-style code.@if, @for, and @switch instead of structural directives (*ngIf, *ngFor).resource() and httpResource() are experimental -- Use only when the project explicitly accepts experimental APIs; otherwise, use HttpClient with toSignal().@defer -- Use it to lazy load heavy or non-critical components to optimize initial bundle size.
</guardrails>
standalone: true@if, @for) is used instead of legacy structural directivessignal, computed, input)inject() function@for loops have a meaningful track expression@defer for lazy loading
</validation>
development
Use when tracing execution paths, mapping dependencies, understanding unfamiliar code, following data flow, investigating end-to-end behavior, debugging call chains, or deciding which files to read next.
development
Use when reviewing authentication, authorization, user input, secrets, API keys, database queries, file uploads, session management, external API calls, OWASP risks, or data handling attack surface.
testing
Use when analyzing tradeoffs, comparing approaches, weighing options, assessing risks, stress-testing conclusions, identifying blind spots, or applying multiple viewpoints to a decision.
development
Use when reviewing hot paths, slow code, database queries, N+1 risks, memory usage, loops, I/O, caching strategy, concurrency, latency-sensitive paths, or resource efficiency.