Back to skills
SKILL.md
Angular
ASecurityCovers Angular across supported versions (19, 20, and 21): the component model (standalone default), dependency injection, change detection (Zone.js vs zoneless), routing, template syntax (built-in control flow), RxJS integration, forms (reactive, template-driven, and signal forms), build system (esbuild/Vite), and testing (Vitest). Use for \"Angular\", \"angular\", \"NgModule\", \"standalone component\", \"Angular CLI\", \"ng serve\", \"ng build\", \"RxJS Angular\", \"Angular DI\", \"depende...
- 4 stars
- 0 votes
- 0 copies
- 0 views
- Added September 24, 2026
Works with
Security analysis
100/100Pro scans all 14 files and shows the line behind each finding
npx -y skills add chrishuffman5/domain-expert --skill angular --agent claude-codeAre you the author of Angular?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/chrishuffman5-angular)---
name: angular
description: "Covers Angular across supported versions (19, 20, and 21): the component model (standalone default), dependency injection, change detection (Zone.js vs zoneless), routing, template syntax (built-in control flow), RxJS integration, forms (reactive, template-driven, and signal forms), build system (esbuild/Vite), and testing (Vitest). Use for \"Angular\", \"angular\", \"NgModule\", \"standalone component\", \"Angular CLI\", \"ng serve\", \"ng build\", \"RxJS Angular\", \"Angular DI\", \"dependency injection Angular\", \"change detection\", \"Zone.js\", \"zoneless\", \"Angular 19\", \"Angular 20\", \"Angular 21\" questions. Covers signals basics; for deep signals coverage (resource(), rxResource(), signal queries/inputs, zoneless migration) use the `angular-signals` skill instead."
license: MIT
---
# Angular
This skill covers Angular across all supported versions (19, 20, and 21), including:
- Component model: standalone components (default since v17), decorators, view encapsulation, content projection, lifecycle hooks
- Dependency injection: hierarchical injectors, `providedIn: 'root'`, `inject()` function, `InjectionToken`, multi-providers, factory providers
- Change detection: Zone.js model (Default vs OnPush), zoneless change detection (signals-driven), `markForCheck()`, `NgZone.runOutsideAngular()`
- Routing: `provideRouter`, lazy loading (`loadComponent`/`loadChildren`), functional guards, resolvers, route-level render modes
- Template syntax: built-in control flow (`@if`, `@for`, `@switch`, `@defer`), binding forms, template reference variables, content projection, `ng-template`/`ng-container`
- RxJS integration: `HttpClient`, key operators, `async` pipe, `takeUntilDestroyed`, `toSignal`/`toObservable` interop
- Signals: `signal()`, `computed()`, `effect()`, `linkedSignal()`, `resource()`/`rxResource()`, signal inputs/outputs/queries, `model()`
- Forms: Reactive Forms (`FormGroup`/`FormControl`), template-driven (`ngModel`), typed forms, Signal Forms (experimental v21)
- Build system: esbuild + Vite dev server, Angular CLI, `angular.json`, schematics, bundle analysis
- Testing: TestBed, `provideHttpClientTesting`, component harnesses, Karma-to-Vitest migration
- SSR and hydration: route-level render modes, incremental hydration, event replay, `provideClientHydration`
- Angular DevTools: component tree, Profiler, signal inspector
When a question is version-specific, read the matching file under `references/versions/`. When the version is unknown, give general guidance and note where behavior differs across versions.
## How to Approach Tasks
When you receive a request:
1. **Classify** the request type:
- **Troubleshooting** -- Load `references/diagnostics.md`
- **Performance** -- Load `references/best-practices.md` (performance section)
- **Architecture / Design** -- Load `references/architecture.md`
- **Signals** -- Use the `angular-signals` skill
- **Forms** -- Load `references/architecture.md` (forms section) or use the `angular-signals` skill (Signal Forms)
- **SSR / Hydration** -- Load `references/patterns/ssr-hydration.md`
- **Standalone Migration** -- Load `references/patterns/standalone-migration.md`
- **Configuration** -- Reference `assets/angular.json`, `assets/tsconfig.json`, or `assets/eslint.config.js`
2. **Identify version** -- Determine whether the user is on Angular 19, 20, or 21. If unclear, ask. Version matters for signal API stability, zoneless support, forms API, testing infrastructure, and SSR features.
3. **Load context** -- Read the relevant reference file for deep knowledge.
4. **Analyze** -- Apply Angular-specific reasoning, not generic TypeScript advice. Consider the DI hierarchy, change detection strategy, signal reactivity model, and component boundaries.
5. **Recommend** -- Provide actionable guidance with code examples. Prefer idiomatic Angular patterns: standalone components, `inject()` over constructor injection, signals over imperative state, OnPush change detection.
6. **Verify** -- Suggest validation steps (Angular DevTools Profiler, bundle analysis, test assertions).
## Core Expertise
### Component Model
Standalone components are the default since Angular 17. A standalone component imports its dependencies directly -- no NgModule wrapper required.
```typescript
import { Component, signal, computed, ChangeDetectionStrategy } from '@angular/core';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-user-card',
standalone: true,
imports: [RouterLink],
template: `
<div class="card">
<h2>{{ name() }}</h2>
<a [routerLink]="['/users', userId()]">View Profile</a>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UserCardComponent {
name = input.required<string>();
userId = input.required<string>();
}
```
Key decorators: `@Component` (UI building block), `@Directive` (behavior on DOM elements), `@Pipe` (template value transforms), `@Injectable` (DI-managed services).
View encapsulation modes: `Emulated` (default, scoped CSS via attributes), `ShadowDom` (native Shadow DOM), `None` (global CSS).
Content projection via `<ng-content>` with optional `select` attribute enables flexible composition patterns (cards, dialogs, layout shells).
### Dependency Injection
Angular's hierarchical DI system is the framework's core infrastructure. The injector tree flows from platform to root to environment (lazy routes) to component to element.
```typescript
@Injectable({ providedIn: 'root' })
export class AuthService {
private http = inject(HttpClient);
login(credentials: Credentials): Observable<User> {
return this.http.post<User>('/api/login', credentials);
}
}
```
`inject()` (preferred over constructor injection) works in constructors and field initializers. `InjectionToken` handles non-class values. Multi-providers collect multiple implementations under one token. `providedIn: 'root'` enables tree-shaking -- unused services are removed from the bundle.
### Change Detection
**Zone.js model (default through v20):** Zone.js monkey-patches browser async APIs. After any async operation, Angular checks the entire component tree. OnPush limits checks to components with changed input references, events, `async` pipe emissions, or explicit `markForCheck()`.
**Zoneless model (default in v21 for new projects):** No Zone.js. Change detection is driven by signal reads in templates, `markForCheck()`, and `async` pipe. Smaller bundles, fewer unnecessary checks, cleaner stack traces.
```typescript
// Zoneless setup
export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection(),
provideRouter(routes),
]
};
```
Always use `ChangeDetectionStrategy.OnPush` -- it is the single highest-impact performance optimization and prepares components for zoneless migration.
### Routing
```typescript
export const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{
path: 'home',
loadComponent: () => import('./home/home.component').then(m => m.HomeComponent),
},
{
path: 'admin',
canActivate: [authGuard],
loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
},
{ path: '**', component: NotFoundComponent },
];
```
Functional guards (preferred since v14) use `inject()` for service access. `loadComponent` lazy-loads standalone components. `loadChildren` lazy-loads child route arrays. Preloading strategies (`withPreloading(PreloadAllModules)`) improve navigation speed.
### Template Syntax
Built-in control flow (v17+) replaces structural directives as the preferred approach:
```html
@if (user()) {
<h2>{{ user().name }}</h2>
} @else {
<app-spinner />
}
@for (item of items(); track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items found</li>
}
@defer (on viewport) {
<app-heavy-chart />
} @placeholder {
<div>Loading chart...</div>
}
```
`@for` requires a `track` expression (replaces `trackBy`). `@defer` blocks enable lazy loading of template sections with triggers: `on idle`, `on viewport`, `on interaction`, `on hover`, `when condition`.
### RxJS Integration
RxJS is Angular's reactive foundation. `HttpClient` returns Observables. The `async` pipe subscribes, triggers change detection, and unsubscribes automatically. `takeUntilDestroyed` provides cleanup tied to component lifecycle.
```typescript
export class SearchComponent {
private destroyRef = inject(DestroyRef);
private searchControl = new FormControl('');
results$ = this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.searchService.search(term)),
takeUntilDestroyed(this.destroyRef),
);
}
```
`toSignal()` converts Observables to signals. `toObservable()` converts signals to Observables. Both are stable in v20+.
### Forms
**Reactive Forms** (preferred for complex scenarios): `FormGroup`, `FormControl`, `FormBuilder`, typed forms (v14+), `Validators`, async validators.
**Template-driven Forms**: `FormsModule`, `ngModel`, simpler but limited for complex validation.
**Signal Forms** (experimental, v21): `formGroup()`, `formField()`, `formArray()` from `@angular/forms/experimental`. Validation state as computed signals, no `valueChanges` subscriptions needed.
### Build System
esbuild + Vite dev server (default since v17). Builder: `@angular-devkit/build-angular:application`. Angular CLI provides generation (`ng generate`), serving (`ng serve` with HMR), building (`ng build`), testing (`ng test`), and linting (`ng lint`).
## Common Pitfalls
**1. Missing OnPush on components**
Default change detection checks every component on every async event. Set `ChangeDetectionStrategy.OnPush` on every component. Configure schematics to apply it by default.
**2. Forgetting `()` when reading signals in templates**
`{{ count }}` renders the function reference as a string. Must use `{{ count() }}`. TypeScript strict templates catch this at compile time.
**3. Subscribing without cleanup**
Subscriptions that outlive their component cause memory leaks. Use `takeUntilDestroyed`, the `async` pipe, or manual unsubscribe in `ngOnDestroy`. Signals with `toSignal()` clean up automatically.
**4. Importing entire modules instead of individual components**
Importing a barrel module that re-exports everything defeats tree-shaking. Import specific standalone components, directives, and pipes.
**5. NullInjectorError from missing providers**
Forgetting `provideHttpClient()` in app config, missing `providedIn: 'root'` on services, or not importing required modules. Check the injector hierarchy.
**6. ExpressionChangedAfterItHasBeenCheckedError**
Binding value changed after Angular's check pass. Common triggers: setting values in `ngAfterViewInit`, synchronous service mutations during CD. Solutions: move to `ngOnInit`, use `Promise.resolve().then()`, or use signals.
**7. Writing to signals inside `computed()` or `effect()`**
`computed()` must be pure -- signal writes throw at runtime. `effect()` signal writes risk infinite loops. Use `computed()` for derived state, `linkedSignal()` for writable derived state.
**8. Plain property mutations in zoneless mode**
Imperative `this.count++` does not trigger change detection without Zone.js. Use `signal()` for all reactive state.
**9. Not using `track` in `@for` blocks**
`@for` requires a `track` expression. Missing it causes a compilation error. Always track by a stable unique identifier (e.g., `item.id`).
**10. Placing `@defer` without SSR consideration**
`@defer` blocks with `hydrate` triggers require `provideClientHydration(withIncrementalHydration())`. Without it, deferred content is not server-rendered.
## Version-Specific Guidance
| Version | Reference | What's version-specific |
|---|---|---|
| 19 | `references/versions/19.md` | linkedSignal, resource()/rxResource() (experimental), incremental hydration (developer preview), route-level render mode (developer preview), event replay (stable), CSS/template HMR, standalone default implicit |
| 20 | `references/versions/20.md` | All signal APIs stable, zoneless developer preview (stable in 20.2), template HMR stable, route-level render mode stable, incremental hydration stable, template language additions, async redirect functions, NgComponentOutlet enhanced |
| 21 | `references/versions/21.md` | Zoneless by default, Signal Forms (experimental), Vitest default test runner, @angular/aria (developer preview), Angular CLI MCP server, Tailwind CSS default, HammerJS removed |
## Sibling Skill
For full signals coverage, use the `angular-signals` skill: `signal()`, `computed()`, `effect()`, `linkedSignal()`, `resource()`/`rxResource()`, signal inputs/outputs/queries, zoneless migration, RxJS interop, state management patterns, diagnostics.
## Reference Files
Load these when you need deep knowledge for a specific area:
- `references/architecture.md` -- Component model, DI, change detection, modules vs standalone, routing, template syntax, RxJS, build system, forms. Read for "how does X work" questions.
- `references/best-practices.md` -- Component design patterns, state management, performance optimization, testing. Read for design and quality questions.
- `references/diagnostics.md` -- ExpressionChangedAfterItHasBeenCheckedError, NullInjectorError, template parse errors, build issues, performance debugging. Read when troubleshooting errors.
## Configuration References
- `assets/angular.json` -- Annotated workspace configuration with esbuild, SSR, HMR, and schematics defaults
- `assets/tsconfig.json` -- Annotated TypeScript configuration for Angular projects
- `assets/eslint.config.js` -- ESLint flat config for Angular with recommended rules
## Pattern Guides
- `references/patterns/signals.md` -- `signal()`, `computed()`, `effect()`, `linkedSignal()`, `resource()` patterns with examples
- `references/patterns/standalone-migration.md` -- NgModules to standalone step-by-step migration guide
- `references/patterns/ssr-hydration.md` -- Route-level render modes, incremental hydration, event replay
## Diagnostic Scripts
Ready-made build/budget audit (read-only) in `scripts/`.
- `scripts/01-build-budget-audit.sh` -- Bundle budgets, initial size, largest bundles, version health
Files in this skill
- SKILL.md
- assets/angular.json
- assets/eslint.config.js
- assets/tsconfig.json
- references/architecture.md
- references/best-practices.md
- references/diagnostics.md
- references/patterns/signals.md
- references/patterns/ssr-hydration.md
- references/patterns/standalone-migration.md
- references/versions/19.md
- references/versions/20.md
- references/versions/21.md
- scripts/01-build-budget-audit.sh
Attribution
Comments
Loading comments…