Angular 21 : zoneless par défaut et Signal Forms ⚡

Angular 21 : zoneless par défaut et Signal Forms ⚡

Loïc Boutter (lazybobcat)
Loïc Boutter (lazybobcat)

Angular 21 débarque le 20 novembre 2025 avec son lot de nouveautés qui simplifient le développement et améliorent les performances. Zoneless par défaut, Signal Forms expérimentaux, support de l’IA… Cette version met l’accent sur la modernisation et la performance.

⚡️ Zoneless par défaut

C’est LE grand changement d’Angular 21 : les nouvelles applications n’utilisent plus zone.js par défaut. Fini les détections de changement mystérieuses et les stack traces incompréhensibles !

Qu’est-ce que ça change ?

Historiquement, Angular utilisait zone.js pour détecter automatiquement les changements (clic, timer, requête HTTP, etc.). C’était pratique mais gourmand : Angular vérifiait souvent plus que nécessaire.

Avec le mode zoneless, la détection repose sur les signals et les événements du template. Résultat :

  • Moins de vérifications inutiles
  • Bundle plus léger (pas de zone.js)
  • Stack traces plus claires
  • Comportement plus prévisible

Comment l’utiliser ?

Pour les nouvelles applications, c’est automatique avec ng new. Pour migrer une app existante :

import { provideZonelessChangeDetection } from '@angular/core';
 
bootstrapApplication(App, {
  providers: [provideZonelessChangeDetection()],
});

Retirez ensuite zone.js de votre angular.json et de vos polyfills !

Ce qui fonctionne sans changement

  • Les événements de template ((click), (input), etc.)
  • Les signals et computed()
  • Les input() de composant
  • HttpClient avec signals
@Component({
  template: `
    @if (pokemon()) {
      <h2>{{ pokemon().name }} (lvl {{ level() }})</h2>
      <button (click)="levelUp()">Niveau +1</button>
    }
  `,
})
export class PokemonCard {
  pokemon = input.required<Pokemon>();
  level = signal(1);
 
  levelUp() {
    this.level.update((lvl) => lvl + 1);
  }
}

Points d’attention

Si votre code dépend de timers ou callbacks sans signals, ajoutez markForCheck() :

constructor(private cdr: ChangeDetectorRef) {
  setTimeout(() => {
    this.data = newValue;
    this.cdr.markForCheck(); // ← nécessaire en zoneless
  }, 1000);
}

Ou mieux : utilisez des signals !

📝 Signal Forms (expérimental)

Angular 21 introduit une nouvelle approche des formulaires entièrement basée sur les signals. C’est encore expérimental, mais ça promet !

Créer un formulaire

Un formulaire Signal se construit à partir d’un signal contenant les données :

import { form, required, minLength } from '@angular/forms/signals';
 
@Component({
  selector: 'pokemon-form',
  template: `
    <form (submit)="save($event)">
      <label>Nom</label>
      <input [field]="pokemonForm.name" />
 
      <label>Type</label>
      <input [field]="pokemonForm.type" />
 
      <button [disabled]="!pokemonForm().valid()">Enregistrer</button>
    </form>
  `,
})
export class PokemonFormComponent {
  pokemon = signal({ name: '', type: '' });
 
  pokemonForm = form(this.pokemon, (path) => {
    required(path.name, { message: 'Le nom est requis' });
    minLength(path.name, 3, { message: 'Au moins 3 caractères' });
    required(path.type);
  });
 
  save(event: SubmitEvent) {
    event.preventDefault();
    submit(this.pokemonForm, async (form) => {
      const pokemon = form().value();
      return this.pokemonService.save(pokemon);
    });
  }
}

Afficher les erreurs

Les erreurs sont exposées sous forme d’array d’objets :

@if (pokemonForm.name().touched() && !pokemonForm.name().valid()) {
  <div>
    @for (error of pokemonForm.name().errors(); track error.kind) {
      <p>{{ error.message }}</p>
    }
  </div>
}

Validateurs disponibles

  • required(), minLength(), maxLength()
  • min(), max(), email(), pattern()
  • validateStandardSchema() pour Zod, Valibot, etc.

Soumission asynchrone

La fonction submit() gère automatiquement la validation et l’état submitting :

submit(this.pokemonForm, async (form) => {
  const { name, type } = form().value();
  return this.http.post('/api/pokemons', { name, type });
});

Pendant la soumission, pokemonForm().submitting est true et peut être utilisé pour un spinner.

🤖 Angular MCP Server pour l’IA

Angular 21 lance le Angular MCP Server, un outil pour améliorer les workflows avec l’IA. MCP (Model Context Protocol) permet aux LLM d’accéder à votre contexte de projet Angular.

À quoi ça sert ?

  • Génération de code contextualisée
  • Suggestions basées sur votre architecture
  • Assistance pour les migrations
  • Génération de tests cohérents

C’est encore en phase d’adoption, mais c’est un premier pas vers des outils IA plus intelligents pour Angular !

📦 HttpClient par défaut

HttpClient est maintenant fourni par défaut dans les nouvelles applications. Plus besoin d’importer provideHttpClient() manuellement !

// Avant Angular 21
bootstrapApplication(App, {
  providers: [provideHttpClient()],
});
 
// Avec Angular 21
bootstrapApplication(App, {
  providers: [
    // HttpClient est déjà disponible !
  ],
});

🔧 Améliorations du styling avec NgStyle

NgStyle fonctionne désormais parfaitement avec la nouvelle syntaxe de contrôle de flux (@if, @for). Plus de comportements étranges !

@Component({
  template: `
    @for (status of statuses; track status) {
      <p [ngStyle]="{ color: status === 'Capturé' ? 'green' : 'red' }">
        {{ status }}
      </p>
    }
  `,
})
export class StatusList {
  statuses = ['Capturé', 'En liberté', 'Évadé'];
}

Combiné avec les signals, c’est encore plus puissant :

theme = signal('clair');
 
toggleTheme() {
  this.theme.update((t) => (t === 'clair' ? 'sombre' : 'clair'));
}
<div [ngStyle]="{ backgroundColor: theme() === 'sombre' ? '#000' : '#fff' }">Contenu avec thème dynamique</div>

🚀 Ce qui reste à surveiller

Angular 21 prolonge l’ère des signaux :

  • Les Signal Forms sont expérimentaux et l’API peut changer
  • Le mode zoneless est stable mais nécessite des ajustements sur les projets existants

Pour en savoir plus

Retour aux articles