Angular Signals et standalone components : les fondations
Une journée essentielle pour poser les bases d’un Angular moderne, stable et maintenable. Objectif : maîtriser ES2024, TypeScript strict, les composants…
🎯 Objectif du Jour 1
Les Angular Signals et les standalone components sont les bases d’une application Angular moderne. Construire des bases solides pour un code lisible, testable, maintenable et orienté long terme. Vous posez la fondation qui conditionne la qualité du reste de la formation.
🧩 Fil Rouge du Projet
Création d’un mini-catalogue B2B (produits, recherche, interactions) qui évoluera chaque jour.
🧠 Patterns du Jour
Smart/Dumb Components, immuabilité, typage strict, séparation des responsabilités.
Section 1 — JavaScript ES2024 & TypeScript strict
Le but est de maîtriser les nouveautés modernes qui rendent le code plus expressif, plus robuste et beaucoup plus prévisible. **Angular repose sur TypeScript strict**, il est donc impossible d’être productif sans ces concepts.
🚀 Concepts modernes à maîtriser
- Copies immuables de tableaux (ES2023) —
toSorted(),toReversed(),toSpliced(),with(): on transforme sans muter l’original. - Regroupement (ES2024) —
Object.groupBy()/Map.groupBy()remplacent lesreduceillisibles. Promise.withResolvers()(ES2024) — crée une promesse et ses fonctions resolve/reject en une ligne.- Opérateurs modernes — chaînage optionnel
?., coalescence??,structuredClone()pour copier en profondeur. - TypeScript Strict —
strict: true+satisfies: sécurité, meilleure DX, bugs éliminés dès l’écriture. - À surveiller (propositions TC39, pas encore dans le langage) : Pattern Matching, Pipeline Operator
|>, Async Context.
🧰 Exemple TypeScript propre
interface Product {
id: number;
name: string;
price: number;
}
const formatPrice = (p: Product) =>
`${p.price.toFixed(2)} €`;
const isCheap = (p: Product) => p.price < 100;
Erreurs courantes à éviter
- Muter des objets au lieu de créer des copies immuables.
- Désactiver strictNullChecks « temporairement ».
- Utiliser any à la place de types précis.
Section 2 — Architecture des Composants Angular
Angular impose une architecture claire : composants déclaratifs, minimalistes, centrés sur l’affichage et la gestion des événements. La logique métier **n’a rien à faire dans un composant**.
🧱 Anatomie propre d’un composant
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Product } from './product.model';
@Component({
selector: 'app-product-card',
standalone: true, // valeur par défaut à partir d'Angular 19
templateUrl: './product-card.html',
})
export class ProductCardComponent {
@Input({ required: true }) product!: Product;
@Output() add = new EventEmitter<Product>(); // toujours typer l'événement
}
- Dumb Components → rôle : afficher + émettre.
- Smart Components → rôle : charger, orchestrer, appeler les services.
- Encapsulation stricte — le composant doit être autonome et pur.
⚡ Signals (remplace 70% des services avec Subject)
La nouvelle API Reactive « Signals » d’Angular permet un état lisible, prévisible et performant sans Subject / BehaviorSubject.
readonly products = signal<Product[]>([]); // typé : sinon never[] en TS strict
readonly promos = computed(() =>
this.products().filter(p => p.price < 100)
);
// effect() doit être créé dans un contexte d'injection (constructeur ou initialiseur de champ)
private readonly logPromos = effect(() => {
console.log('Promos:', this.promos());
});
Bonnes pratiques
- Un composant = 1 responsabilité.
- Jamais de logique réseau dans un composant de présentation.
- Préférer les fonctions pures et l’immuabilité.
- Utiliser Signals → lisibilité + performance.
Astuce d’expert
Depuis Angular 17.1, préférez les fonctions input(), input.required<T>() et output<T>() aux décorateurs @Input/@Output : les entrées deviennent des signals, utilisables directement dans computed(), et le composant passe naturellement en ChangeDetectionStrategy.OnPush.
Ateliers pratiques du Jour 1
Atelier 1 — Construire ProductCard (Dumb)
Objectif : un composant de présentation pur, sans aucune logique métier.
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CurrencyPipe } from '@angular/common';
import { Product } from './product.model';
@Component({
selector: 'app-product-card',
standalone: true,
imports: [CurrencyPipe],
template: `
<article class="card">
<h3>{{ product.name }}</h3>
<p>{{ product.price | currency:'EUR' }}</p>
<button type="button" (click)="add.emit(product)">Ajouter</button>
</article>
`
})
export class ProductCardComponent {
@Input({ required: true }) product!: Product;
@Output() add = new EventEmitter<Product>();
}
Atelier 2 — Construire ProductList (Smart)
Ici, on orchestre les données, les appels réseau, la gestion du panier. C’est le cerveau, pas l’UI.
import { Component, inject } from '@angular/core';
import { ProductCardComponent } from './product-card.component';
import { CartService } from './cart.service';
import { Product } from './product.model';
@Component({
selector: 'app-product-list',
standalone: true,
imports: [ProductCardComponent],
template: `
@for (p of products; track p.id) {
<app-product-card [product]="p" (add)="addToCart($event)" />
}
`
})
export class ProductListComponent {
products: Product[] = [];
private readonly cart = inject(CartService);
addToCart(p: Product) {
this.cart.add(p);
}
}
Atelier 3 — Refactor complet ES5 → ES2024 → TS strict
Mise à niveau du style, lisibilité, sécurité.
Astuce d’expert
Activez strictTemplates: true dans angularCompilerOptions : le compilateur vérifie alors les types des bindings dans les templates ([product], $event…). C’est le complément indispensable de strict: true côté TypeScript.
Patterns & Anti-patterns du Jour 1
✔ Bonnes pratiques
- Séparer Smart et Dumb Components.
- Immuabilité systématique.
- Typage strict (jamais any).
- Utilisation de Signals pour gérer l’état proprement.
- Favoriser l’injection de services dans Smart Components.
❌ Anti-patterns
- Mettre des appels réseau dans un composant de présentation.
- Accumuler les subscribe() non gérés.
- Laisser Angular compiler sans TypeScript strict.
- Muter les tableaux utilisés dans @for / *ngFor.
- CSS global qui fuit dans l’app.
Suivez votre progression. Créez un compte gratuit pour cocher vos leçons et reprendre là où vous en étiez.
Créer un compte gratuit