Aller au contenu principal

Syntaxe de Template

Interpolation

<!-- String interpolation -->
<p>{{ title }}</p>
<p>{{ user.name }}</p>
<p>{{ 1 + 1 }}</p>
<p>{{ getName() }}</p>
<p>{{ user?.name }}</p> <!-- Safe navigation -->

Property Binding

<!-- Element property -->
<img [src]="imageUrl">
<button [disabled]="isDisabled">Click</button>
<input [value]="username">

<!-- Class binding -->
<div [class.active]="isActive">Text</div>
<div [class]="'btn btn-primary'">Button</div>
<div [class]="classes">Dynamic classes</div>

<!-- Style binding -->
<div [style.color]="textColor">Colored text</div>
<div [style.font-size.px]="fontSize">Text</div>
<div [style]="{'color': 'red', 'font-size': '14px'}">Styled</div>

<!-- Attribute binding -->
<button [attr.aria-label]="label">Button</button>
<div [attr.data-id]="userId">Data</div>

Event Binding

<!-- Basic events -->
<button (click)="onClick()">Click</button>
<input (input)="onInput($event)">
<input (keyup)="onKeyup($event)">
<input (keyup.enter)="onEnter()">

<!-- Event with parameter -->
<button (click)="onDelete(user.id)">Delete</button>

<!-- $event object -->
<input (input)="onInput($event)">

<!-- Multiple events -->
<input
(focus)="onFocus()"
(blur)="onBlur()"
(input)="onInput($event)">
onInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
console.log(value);
}

Two-way Binding

<!-- Using ngModel (FormsModule) -->
<input [(ngModel)]="username">

<!-- Equivalent au: -->
<input
[ngModel]="username"
(ngModelChange)="username = $event">

<!-- Liaison bidirectionnelle personnalisée -->
<app-counter [(count)]="counter"></app-counter>
// Liaison bidirectionnelle personnalisée dans le composant (basée sur signal)
import { Component, input, output } from '@angular/core';

@Component({
selector: 'app-counter',
standalone: true
})
export class CounterComponent {
count = input<number>(0);
countChange = output<number>();

increment() {
const newCount = this.count() + 1;
this.countChange.emit(newCount);
}
}

Template Reference Variables

<!-- Element reference -->
<input #nameInput>
<button (click)="nameInput.focus()">Focus</button>

<!-- Component reference -->
<app-child #child></app-child>
<button (click)="child.doSomething()">Do</button>

<!-- Form reference -->
<form #userForm="ngForm">
<input name="username" ngModel>
<button [disabled]="userForm.invalid">Submit</button>
</form>

<!-- Directive reference -->
<div appHighlight #highlight="appHighlight">
<button (click)="highlight.changeColor('red')">Red</button>
</div>

Structural Directives

<!-- *ngIf -->
<div *ngIf="isVisible">Visible</div>
<div *ngIf="user; else loading">{{ user.name }}</div>
<ng-template #loading>Loading...</ng-template>

<!-- *ngIf with as -->
<div *ngIf="user$ | async as user">
{{ user.name }}
</div>

<!-- *ngFor -->
<li *ngFor="let item of items">{{ item }}</li>
<li *ngFor="let item of items; index as i">
{{ i }}: {{ item }}
</li>
<li *ngFor="let item of items; trackBy: trackByFn">
{{ item.name }}
</li>

<!-- *ngFor variables -->
<div *ngFor="let item of items;
index as i;
first as isFirst;
last as isLast;
even as isEven;
odd as isOdd">
{{ i }}: {{ item }}
</div>

<!-- *ngSwitch -->
<div [ngSwitch]="status">
<p *ngSwitchCase="'active'">Active</p>
<p *ngSwitchCase="'inactive'">Inactive</p>
<p *ngSwitchDefault>Unknown</p>
</div>

ng-template

<!-- Basic template -->
<ng-template #template>
<p>Template content</p>
</ng-template>

<!-- Conditional template -->
<div *ngIf="show; else elseBlock">Main content</div>
<ng-template #elseBlock>
<p>Else content</p>
</ng-template>

<!-- Template with context -->
<ng-template #userTemplate let-user let-i="index">
<p>{{ i }}: {{ user.name }}</p>
</ng-template>

<!-- Utiliser with ngTemplateOutlet -->
<ng-container *ngTemplateOutlet="userTemplate; context: { $implicit: user, index: 0 }">
</ng-container>

ng-container

<!-- Grouping without extra DOM element -->
<ng-container *ngIf="isVisible">
<h1>Title</h1>
<p>Content</p>
</ng-container>

<!-- Multiple structural directives -->
<ng-container *ngFor="let item of items">
<div *ngIf="item.active">{{ item.name }}</div>
</ng-container>

ng-content

<!-- Component with content projection -->
@Component({
selector: 'app-card',
standalone: true,
template: `
<div class="card">
<ng-content></ng-content>
</div>
`
})

<!-- Usage -->
<app-card>
<h2>Card Title</h2>
<p>Card content</p>
</app-card>

<!-- Multiple slots -->
@Component({
selector: 'app-layout',
standalone: true,
template: `
<header>
<ng-content select="[slot=header]"></ng-content>
</header>
<main>
<ng-content></ng-content>
</main>
<footer>
<ng-content select="[slot=footer]"></ng-content>
</footer>
`
})

<!-- Usage -->
<app-layout>
<div slot="header">Header content</div>
<p>Main content</p>
<div slot="footer">Footer content</div>
</app-layout>

Pipes in Templates

<!-- Basic pipe -->
<p>{{ name | uppercase }}</p>

<!-- Pipe with parameters -->
<p>{{ price | currency:'USD':'symbol':'1.2-2' }}</p>

<!-- Chained pipes -->
<p>{{ date | date:'short' | uppercase }}</p>

<!-- Async pipe -->
<p>{{ user$ | async }}</p>
<div *ngIf="users$ | async as users">
<p *ngFor="let user of users">{{ user.name }}</p>
</div>

Safe Navigation Operator

<!-- Safe navigation (?) -->
<p>{{ user?.name }}</p>
<p>{{ user?.address?.city }}</p>

<!-- Without safe navigation (throws error if null) -->
<p>{{ user.name }}</p> <!-- Error if user is null -->

<!-- Combination with async -->
<p>{{ (user$ | async)?.name }}</p>

Non-null Assertion

<!-- Non-null assertion (!) -->
<p>{{ user!.name }}</p> <!-- Tells TS user is not null -->

Template Expressions

<!-- Arithmetic -->
<p>{{ 1 + 1 }}</p>
<p>{{ price * quantity }}</p>

<!-- Comparison -->
<p>{{ count > 10 }}</p>
<p>{{ status === 'active' }}</p>

<!-- Logical -->
<p>{{ isActive && isVisible }}</p>
<p>{{ user || 'Guest' }}</p>

<!-- Ternary -->
<p>{{ isActive ? 'Active' : 'Inactive' }}</p>

<!-- Method calls -->
<p>{{ getFullName() }}</p>
<p>{{ user.getName() }}</p>

<!-- Limitations: -->
<!-- ❌ No assignments: count = 5 -->
<!-- ❌ No new/typeof/instanceof -->
<!-- ❌ No chaining: ; -->
<!-- ❌ No increment/decrement: count++ -->

Bonnes Pratiques

  • Garder template expressions simple
  • Utiliser pipes for transformations
  • Utiliser trackBy with *ngFor
  • Utiliser async pipe for observables
  • Utiliser safe navigation for nullable values
  • Éviter la logique complexe dans les templates (déplacer vers le composant)
  • Utiliser ng-container pour éviter des éléments DOM supplémentaires
  • Utiliser ng-template for reusable templates