Files
ISA-Frontend/.claude/skills/angular-template/SKILL.md
Lorenz Hilpert bf30ec1213 feat(skills): create html-template skill for E2E and ARIA attributes
- Create comprehensive html-template skill with 3 reference files
  - E2E Testing Attributes (data-what, data-which patterns)
  - ARIA Accessibility Attributes (roles, properties, states, WCAG)
  - Combined Patterns (real-world examples with both)
- Move E2E attribute guidance from command to skill
- Add extensive ARIA accessibility documentation
- Update angular-template skill with cross-references
- Remove dev-add-e2e-attrs command (functionality now in skill)

The new skill provides 3,322 lines of comprehensive documentation
covering both testing and accessibility best practices for HTML
templates, with practical examples for forms, navigation, tables,
dialogs, and more.

Benefits:
- Clean separation: Angular syntax vs HTML attributes
- Reusable: html-template works with any HTML context
- Comprehensive: E2E + ARIA in one place
- Integrated: Works seamlessly with angular-template skill
2025-10-29 13:21:29 +01:00

6.3 KiB

name, description
name description
angular-template 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 Skill: For E2E testing attributes (data-what, data-which) and ARIA accessibility attributes, see the html-template skill. Both skills work together when writing Angular templates.

Control Flow (Angular 17+)

@if / @else if / @else

@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

@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

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

@defer Lazy Loading

Basic Usage

@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

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

Multi-Slot with Selectors

@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:

<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

<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:

myTemplate = viewChild<TemplateRef<unknown>>('myTemplate');

ng-container

Groups elements without DOM footprint:

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

Variables

@let (Angular 18.1+)

@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 (#)

<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 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/