---
name: angular-template
description: This skill should be used when writing or reviewing Angular component templates. It provides guidance on modern Angular 20+ template syntax including control flow (@if, @for, @switch, @defer), content projection (ng-content), template references (ng-template, ng-container), variable declarations (@let), and expression binding. Use when creating components, refactoring to modern syntax, implementing lazy loading, or reviewing template best practices.
---

# Angular Template

Guide for modern Angular 20+ template patterns: control flow, lazy loading, projection, and binding.

## When to Use

- Creating/reviewing component templates
- Refactoring legacy `*ngIf/*ngFor/*ngSwitch` to modern syntax
- Implementing `@defer` lazy loading
- Designing reusable components with `ng-content`
- Template performance optimization

**Related Skills:** These skills work together when writing Angular templates:
- **[html-template](../html-template/SKILL.md)** - E2E testing attributes (`data-what`, `data-which`) and ARIA accessibility
- **[tailwind](../tailwind/SKILL.md)** - ISA design system styling (colors, typography, spacing, layout)

## Control Flow (Angular 17+)

### @if / @else if / @else

```typescript
@if (user.isAdmin()) {
  <app-admin-dashboard />
} @else if (user.isEditor()) {
  <app-editor-dashboard />
} @else {
  <app-viewer-dashboard />
}

// Store result with 'as'
@if (user.profile?.settings; as settings) {
  <p>Theme: {{settings.theme}}</p>
}
```

### @for with @empty

```typescript
@for (product of products(); track product.id) {
  <app-product-card [product]="product" />
} @empty {
  <p>No products available</p>
}
```

**CRITICAL:** Always provide `track` expression:
- Best: `track item.id` or `track item.uuid`
- Static lists: `track $index`
- **NEVER:** `track identity(item)` (causes full re-render)

**Contextual variables:** `$count`, `$index`, `$first`, `$last`, `$even`, `$odd`

### @switch

```typescript
@switch (viewMode()) {
  @case ('grid') { <app-grid-view /> }
  @case ('list') { <app-list-view /> }
  @default { <app-grid-view /> }
}
```

## @defer Lazy Loading

### Basic Usage

```typescript
@defer (on viewport) {
  <app-heavy-chart />
} @placeholder (minimum 500ms) {
  <div class="skeleton"></div>
} @loading (after 100ms; minimum 1s) {
  <mat-spinner />
} @error {
  <p>Failed to load</p>
}
```

### Triggers

| Trigger | Use Case |
|---------|----------|
| `idle` (default) | Non-critical features |
| `viewport` | Below-the-fold content |
| `interaction` | User-initiated (click/keydown) |
| `hover` | Tooltips/popovers |
| `timer(Xs)` | Delayed content |
| `when(expr)` | Custom condition |

**Multiple triggers:** `@defer (on interaction; on timer(5s))`
**Prefetching:** `@defer (on interaction; prefetch on idle)`

### Requirements

- Components **MUST be standalone**
- No `@ViewChild`/`@ContentChild` references
- Reserve space in `@placeholder` to prevent layout shift

### Best Practices

- ✅ Defer below-the-fold content
- ❌ Never defer above-the-fold (harms LCP)
- ❌ Avoid `immediate`/`timer` during initial render (harms TTI)
- Test with network throttling

## Content Projection

### Single Slot

```typescript
@Component({
  selector: 'ui-card',
  template: `<div class="card"><ng-content></ng-content></div>`
})
```

### Multi-Slot with Selectors

```typescript
@Component({
  template: `
    <header><ng-content select="card-header"></ng-content></header>
    <main><ng-content select="card-body"></ng-content></main>
    <footer><ng-content></ng-content></footer> <!-- default slot -->
  `
})
```

**Usage:**
```html
<ui-card>
  <card-header><h3>Title</h3></card-header>
  <card-body><p>Content</p></card-body>
  <button>Action</button> <!-- goes to default slot -->
</ui-card>
```

**Fallback content:** `<ng-content select="title">Default Title</ng-content>`
**Aliasing:** `<h3 ngProjectAs="card-header">Title</h3>`

### CRITICAL Constraint

`ng-content` **always instantiates** (even if hidden). For conditional projection, use `ng-template` + `NgTemplateOutlet`.

## Template References

### ng-template

```html
<ng-template #userCard let-user="userData" let-index="i">
  <div class="user">#{{index}}: {{user.name}}</div>
</ng-template>

<ng-container
  *ngTemplateOutlet="userCard; context: {userData: currentUser(), i: 0}">
</ng-container>
```

**Access in component:**
```typescript
myTemplate = viewChild<TemplateRef<unknown>>('myTemplate');
```

### ng-container

Groups elements without DOM footprint:

```html
<p>
  Hero's name is
  <ng-container @if="hero()">{{hero().name}}</ng-container>.
</p>
```

## Variables

### @let (Angular 18.1+)

```typescript
@let userName = user().name;
@let greeting = 'Hello, ' + userName;
@let asyncData = data$ | async;

<h1>{{greeting}}</h1>
```

**Scoped to current view** (not hoisted to parent/sibling).

### Template References (#)

```html
<input #emailInput type="email" />
<button (click)="sendEmail(emailInput.value)">Send</button>

<app-datepicker #startDate />
<button (click)="startDate.open()">Open</button>
```

## Binding Patterns

**Property:** `[disabled]="!isValid()"`
**Attribute:** `[attr.aria-label]="label()"` `[attr.data-what]="'card'"`
**Event:** `(click)="save()"` `(input)="onInput($event)"`
**Two-way:** `[(ngModel)]="userName"`
**Class:** `[class.active]="isActive()"` or `[class]="{active: isActive()}"`
**Style:** `[style.width.px]="width()"` or `[style]="{color: textColor()}"`

## Best Practices

1. **Use signals:** `isExpanded = signal(false)`
2. **Prefer control flow over directives:** Use `@if` not `*ngIf`
3. **Keep expressions simple:** Use `computed()` for complex logic
4. **Testing & Accessibility:** Always add E2E and ARIA attributes (see **[html-template](../html-template/SKILL.md)** skill)
5. **Track expressions:** Required in `@for`, use unique IDs

## Migration

| Legacy | Modern |
|--------|--------|
| `*ngIf="condition"` | `@if (condition) { }` |
| `*ngFor="let item of items"` | `@for (item of items; track item.id) { }` |
| `[ngSwitch]` | `@switch (value) { @case ('a') { } }` |

**CLI migration:** `ng generate @angular/core:control-flow`

## Reference Files

For detailed examples and edge cases, see:
- `references/control-flow-reference.md` - @if/@for/@switch patterns
- `references/defer-patterns.md` - Lazy loading strategies
- `references/projection-patterns.md` - Advanced ng-content
- `references/template-reference.md` - ng-template/ng-container

Search with: `grep -r "pattern" references/`
