Angular 21 marque une étape charnière dans l'évolution du framework de Google. Après plusieurs versions de préparation, les fonctionnalités majeures introduites depuis Angular 16 atteignent leur pleine maturité. Signals, la détection de changements sans Zone.js, et une expérience développeur radicalement améliorée sont au cœur de cette release.

1. L'API Signals — Pleinement stable

Introduite en preview dans Angular 16, l'API Signals est désormais 100% stable dans Angular 21. Elle remplace le modèle de détection de changements basé sur Zone.js par un système réactif fin-grained.

Signal inputs, outputs et queries

TypeScript
import { Component, input, output, model, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <button (click)="decrement()">-</button>
    <span>{{ count() }}</span>
    <button (click)="increment()">+</button>
  `
})
export class CounterComponent {
  // Signal input (remplace @Input)
  initialValue = input<number>(0);

  // Signal output (remplace @Output + EventEmitter)
  changed = output<number>();

  // Two-way binding avec model()
  count = model(0);

  increment() {
    this.count.update(v => v + 1);
    this.changed.emit(this.count());
  }

  decrement() { this.count.update(v => v - 1); }
}

computed() et effect()

TypeScript
import { signal, computed, effect } from '@angular/core';

const firstName = signal('Kriss');
const lastName  = signal('Clotilde');

// Valeur dérivée recalculée automatiquement
const fullName = computed(() => `${firstName()} ${lastName()}`);

// Effet qui s'exécute à chaque changement
effect(() => {
  console.log('Nom complet :', fullName());
});

firstName.set('Jean'); // → 'Jean Clotilde'

2. Zoneless — Détection de changements sans Zone.js

La détection de changements zoneless est maintenant stable et recommandée pour les nouvelles applications. Fini le monkey-patching de toutes les API asynchrones du navigateur par Zone.js.

TypeScript — app.config.ts
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
// En Angular 21, plus besoin du préfixe "Experimental"
import { provideZonelessChangeDetection } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZonelessChangeDetection(),
    provideRouter(routes),
  ]
};

En mode zoneless, la vue ne se met à jour que lorsqu'un Signal change, ou lorsque vous appelez manuellement ChangeDetectorRef.markForCheck(). Les performances sont nettement améliorées sur les grandes applications.

3. La syntaxe @let dans les templates

Angular 21 stabilise la déclaration de variables locales dans les templates avec @let, une longue demande de la communauté.

HTML — Template Angular
<!-- Avant Angular 17 : utilisation de ng-template -->
<ng-container *ngLet="user$ | async as user">
  {{ user?.name }}
</ng-container>

<!-- Angular 21 — @let natif -->
@let user = userSignal();
@let fullName = user.firstName + ' ' + user.lastName;

<h1>Bonjour, {{ fullName }} !</h1>

@if (user.isAdmin) {
  @let adminLabel = 'Administrateur';
  <span class="badge">{{ adminLabel }}</span>
}

4. Resource API — Gestion async déclarative

La Resource API simplifie radicalement le chargement de données asynchrones. Elle remplace les patterns complexes à base de ngOnInit + Observable + gestion manuelle du loading/error.

TypeScript
import { resource, signal } from '@angular/core';

@Component({
  template: `
    @if (userResource.isLoading()) {
      <p>Chargement...</p>
    } @else if (userResource.error()) {
      <p>Erreur : {{ userResource.error() }}</p>
    } @else {
      <p>{{ userResource.value()?.name }}</p>
    }
  `
})
export class UserComponent {
  userId = signal(1);

  // Recharge automatiquement quand userId change
  userResource = resource({
    request: () => ({ id: this.userId() }),
    loader: ({ request }) =>
      fetch(`/api/users/${request.id}`).then(r => r.json())
  });
}

5. Hydratation incrémentale stable

L'hydratation incrémentale, annoncée en preview dans Angular 19, est désormais stable. Elle permet de n'hydrater les composants que lorsqu'ils entrent dans le viewport ou lors d'une interaction utilisateur.

HTML — Template
<!-- Hydratation différée au survol -->
@defer (hydrate on hover) {
  <app-heavy-chart [data]="chartData()" />
}

<!-- Hydratation au viewport -->
@defer (hydrate on viewport) {
  <app-comments-section />
}

<!-- Toujours pré-rendu en SSR, hydraté à la demande -->
@defer (hydrate on interaction) {
  <app-user-settings />
}

6. Récapitulatif des nouveautés Angular 21

Signals stable

signal(), computed(), effect(), input(), output(), model() — tout est stable et recommandé.

🚀

Zoneless stable

Plus de Zone.js. Détection de changements fine-grained, meilleures performances.

📦

Resource API

Chargement de données asynchrones avec états loading/error intégrés.

🔤

@let en template

Déclaration de variables locales directement dans les templates HTML.

💧

Hydratation incrémentale

Hydratation SSR conditionnelle par viewport, hover ou interaction.

🛠️

HMR amélioré

Hot Module Replacement plus rapide avec support des Signals et styles à la volée.

7. Migration depuis Angular 17-20

La migration est facilitée par le schematic officiel. Les composants basés sur @Input() / @Output() continuent de fonctionner sans modification. La migration peut se faire progressivement, composant par composant.

Shell
# Mise à jour vers Angular 21
ng update @angular/core@21 @angular/cli@21

# Migration automatique vers signal inputs
ng generate @angular/core:signal-input-migration

# Migration vers signal queries
ng generate @angular/core:signal-queries-migration

Attention : Zone.js est toujours supporté mais entre progressivement en mode "maintenance". Il est recommandé de planifier la migration vers le mode zoneless pour les nouvelles fonctionnalités et optimisations futures.