À propos de ce cours
Cette leçon présente les directives NgClass et NgStyle : chaîne, tableau, Set ou objet de classes, objet de styles avec suffixes d’unité et valeur null. Elle les compare aux liaisons [class], [class.x], [style] et [style.x.unité], qui ne demandent aucun import. Elle montre trois différences silencieuses entre les deux écritures, la combinaison de plusieurs sources et un thème piloté par des variables CSS. Elle couvre enfin les migrations ngclass-to-class et ngstyle-to-style, sept activités corrigées et les erreurs fréquentes.
-
Objectifs
- Ajouter et retirer des classes CSS avec
[ngClass]: chaîne, tableau,Setet objet - Appliquer des styles calculés avec
[ngStyle], avec ou sans unité - Obtenir les mêmes résultats avec les liaisons
[class],[class.nom],[style]et[style.propriété.unité] - Connaître les différences silencieuses entre les directives et les liaisons
- Piloter un thème avec des variables CSS liées sur l'hôte
- Migrer un projet avec les commandes officielles
- Ajouter et retirer des classes CSS avec
-
Présentation
- Une interface change d'apparence selon son état : un bouton actif, une note en échec, une barre de progression, un thème sombre… Angular propose deux outils pour cela : les directives d'attribut
NgClassetNgStyle, présentes depuis la première version, et les liaisons[class]et[style], intégrées au langage des templates. - Contrairement à
NgIfouNgFor,NgClassetNgStylene sont pas dépréciées en Angular 22. Mais la documentation recommande les liaisons pour les cas simples, car elles ne demandent aucun import, et Angular fournit des commandes pour migrer. Vous rencontrerez donc les deux écritures : il faut savoir lire l'une et choisir l'autre en connaissance de cause.
- Une interface change d'apparence selon son état : un bouton actif, une note en échec, une barre de progression, un thème sombre… Angular propose deux outils pour cela : les directives d'attribut
- Pour suivre ce chapitre, continuez dans le projet
premiers-pas-angularet créez un dossiersrc/app/apparence/. Tous les exemples ont été compilés et testés avec Angular 22.2 (composantsOnPushpar défaut, sans Zone.js).
-
La directive ngClass
[ngClass]ajoute des classes à l'élément, sans toucher à ses classes statiques (class="boite"). Elle accepte :- une chaîne: les classes séparées par des espaces (
'arrondi ombre') ; - un tableau ou un Set de noms de classes ;
- un objet
{ classe: condition }: la classe est présente quand la condition est vraie. Une clé peut contenir plusieurs classes ('gras souligne').
- une chaîne: les classes séparées par des espaces (
- La directive s'importe depuis
@angular/common:
import { Component, signal } from '@angular/core';
import { NgClass } from '@angular/common';
// [ngClass] accepte une chaîne, un tableau, un Set ou un objet { classe: condition }
@Component({
selector: 'app-ngclass',
imports: [NgClass],
template: `
<button (click)="actif.set(!actif())">Activer / désactiver</button>
<button (click)="note.set(note() >= 10 ? 7 : 15)">Note : {{ note() }}</button>
<p class="boite" [ngClass]="'arrondi ombre'">Chaîne : « arrondi ombre »</p>
<p class="boite" [ngClass]="['arrondi', actif() ? 'actif' : 'inactif']">Tableau</p>
<p class="boite" [ngClass]="{ actif: actif(), alerte: note() < 10, 'gras souligne': note() >= 10 }">
Objet : actif, alerte, « gras souligne »
</p>
`,
styles: `
.boite { padding: 6px 10px; border: 1px solid #cbd5e1; margin: 6px 0; }
.arrondi { border-radius: 12px; } .ombre { box-shadow: 0 3px 8px #0003; }
.actif { border-color: #3178c6; background: #e8f1fb; } .inactif { color: #94a3b8; }
.alerte { background: #fde8e8; color: #b91c1c; } .gras { font-weight: 700; } .souligne { text-decoration: underline; }
`,
})
export class NgClassExemple {
protected readonly actif = signal(true);
protected readonly note = signal(15);
}

- Cliquez sur les deux boutons : la deuxième carte passe de
actifàinactif, la troisième perdgras souligneet prendalerte.

- Sans l'import de
NgClass, la compilation échoue :

-
La directive ngStyle
[ngStyle]reçoit un objet{ propriété: valeur }. Un suffixe d'unité dans la clé ('font-size.px','width.%','margin.em') évite de concaténer l'unité à la main.- Une valeur
nullouundefinedretire la propriété :
import { Component, signal } from '@angular/core';
import { NgStyle } from '@angular/common';
// [ngStyle] : un objet { propriété: valeur } ; un suffixe .px, .%, .em… ajoute l'unité ; null retire le style
@Component({
selector: 'app-ngstyle',
imports: [NgStyle],
template: `
<label>Taille : <input type="range" min="12" max="40" [value]="taille()" (input)="taille.set(+$any($event.target).value)" /> {{ taille() }} px</label>
<label>Couleur : <input type="color" [value]="couleur()" (input)="couleur.set($any($event.target).value)" /></label>
<label><input type="checkbox" [checked]="encadre()" (change)="encadre.set(!encadre())" /> encadré</label>
<p [ngStyle]="{
'font-size.px': taille(),
color: couleur(),
'width.%': 80,
border: encadre() ? '2px dashed ' + couleur() : null
}">Angular 22</p>
`,
styles: `label { margin-right: 12px; } p { margin: 8px 0; padding: 4px 8px; font-weight: 700; }`,
})
export class NgStyleExemple {
protected readonly taille = signal(24);
protected readonly couleur = signal('#dd0031');
protected readonly encadre = signal(true);
}

- Décochez « encadré » : l'attribut
styledevientfont-size: 32px; color: rgb(221, 0, 49); width: 80%;, sansborder.

-
Les liaisons [class] et [style]
- Les mêmes résultats s'obtiennent avec les liaisons de classe et de style, déjà vues dans la leçon sur le property binding, sans aucun import:
[class.actif]="condition": une classe ;[class]="…": plusieurs classes, avec un objet, un tableau ou une chaîne ;[style.color],[style.width.px],[style.width.%]: une propriété, l'unité dans le nom de la liaison ;[style]="{ … }": plusieurs propriétés, chaque valeur avec son unité.
- Un
computed()qui renvoie l'objet de classes ou de styles garde le template court et se teste facilement :
- Les mêmes résultats s'obtiennent avec les liaisons de classe et de style, déjà vues dans la leçon sur le property binding, sans aucun import:
import { Component, computed, signal } from '@angular/core';
// Les mêmes résultats avec les liaisons [class] et [style] : aucun import
@Component({
selector: 'app-liaisons',
template: `
<button (click)="actif.set(!actif())">Activer / désactiver</button>
<button (click)="taille.set(taille() === 24 ? 16 : 24)">Taille : {{ taille() }} px</button>
<!-- une seule classe -->
<p class="boite" [class.actif]="actif()">[class.actif]</p>
<!-- plusieurs classes : objet, tableau ou chaîne -->
<p class="boite" [class]="{ actif: actif(), arrondi: true }">[class] avec un objet</p>
<p class="boite" [class]="['arrondi', actif() ? 'actif' : 'inactif']">[class] avec un tableau</p>
<p class="boite" [class]="classes()">[class] avec un computed</p>
<!-- un style, avec son unité -->
<p [style.font-size.px]="taille()" [style.color]="actif() ? '#3178c6' : null">[style.font-size.px]</p>
<!-- plusieurs styles : objet (sans suffixe d'unité) -->
<p [style]="{ 'font-size': taille() + 'px', 'letter-spacing': '2px', color: '#16a34a' }">[style] avec un objet</p>
`,
styles: `
.boite { padding: 6px 10px; border: 1px solid #cbd5e1; margin: 6px 0; }
.arrondi { border-radius: 12px; } .actif { border-color: #3178c6; background: #e8f1fb; }
.inactif { color: #94a3b8; } .alerte { background: #fde8e8; } p { margin: 6px 0; }
`,
})
export class Liaisons {
protected readonly actif = signal(true);
protected readonly taille = signal(24);
protected readonly classes = computed(() => ({ actif: this.actif(), arrondi: true, alerte: !this.actif() }));
}
![Résultat des liaisons [class] et [style]](https://apcpedagogie.com/wp-content/uploads/2026/11/angular-exemple-liaisons.png)
![Correspondance entre ngClass et la liaison [class]](https://apcpedagogie.com/wp-content/uploads/2026/11/angular-ngclass-et-class.png)
![Correspondance entre ngStyle et la liaison [style]](https://apcpedagogie.com/wp-content/uploads/2026/11/angular-ngstyle-et-style.png)
-
Trois différences silencieuses
- Les directives et les liaisons ne se comportent pas exactement de la même façon. Ces différences ne provoquent aucune erreur, ni à la compilation ni dans la console : le style n'est simplement pas appliqué. L'exemple suivant les met côte à côte et affiche les classes et la largeur réellement obtenues :
import { Component, signal } from '@angular/core';
import { NgClass, NgStyle } from '@angular/common';
// Trois différences entre les directives et les liaisons, sans aucun message d'erreur
@Component({
selector: 'app-pieges',
imports: [NgClass, NgStyle],
template: `
<table>
<tr><th></th><th>[ngClass] / [ngStyle]</th><th>[class] / [style]</th></tr>
<tr><td>Clé « gras souligne »</td>
<td><span #a [ngClass]="{ 'gras souligne': true }">texte</span> <code>{{ a.className || '∅' }}</code></td>
<td><span #b [class]="{ 'gras souligne': true }">texte</span> <code>{{ b.className || '∅' }}</code></td></tr>
<tr><td>Clé « width.px »</td>
<td><span #c class="barre" [ngStyle]="{ 'width.px': 90 }"></span> <code>{{ c.style.width || '∅' }}</code></td>
<td><span #d class="barre" [style]="{ 'width.px': 90 }"></span> <code>{{ d.style.width || '∅' }}</code></td></tr>
<tr><td>Objet modifié sur place</td>
<td><span #e [ngClass]="etats">texte</span> <code>{{ e.className || '∅' }}</code></td>
<td><span #f [class]="etats">texte</span> <code>{{ f.className || '∅' }}</code></td></tr>
</table>
<button (click)="muter()">etats['alerte'] = true</button>
<button (click)="remplacer()">etats = nouvel objet</button>
<small>(tours d'affichage : {{ tours() }})</small>
`,
styles: `
table { border-collapse: collapse; } td, th { border: 1px solid #e2e8f0; padding: 4px 8px; text-align: left; }
.gras { font-weight: 700; } .souligne { text-decoration: underline; } .alerte { color: #dc2626; }
.barre { display: inline-block; height: 10px; background: #3178c6; vertical-align: middle; min-width: 2px; }
code { color: #64748b; font-size: 12px; margin-left: 6px; }
`,
})
export class Pieges {
protected etats: Record<string, boolean> = { gras: true, alerte: false };
protected readonly tours = signal(0);
protected muter() {
this.etats['alerte'] = true; // même objet : seul NgClass (ngDoCheck) voit le changement
this.tours.update((n) => n + 1);
}
protected remplacer() {
this.etats = { ...this.etats }; // nouvel objet : la liaison [class] le voit aussi
this.tours.update((n) => n + 1);
}
}
![Les trois différences entre ngClass/ngStyle et [class]/[style]](https://apcpedagogie.com/wp-content/uploads/2026/11/angular-exemple-pieges.png)
- Clé à plusieurs classes:
[ngClass]ajoutegras souligne,[class]n'ajoute rien. Avec la liaison, écrivez une clé par classe. - Suffixe d'unité:
[ngStyle]donnewidth: 90px,[style]ignore la clé'width.px'. Avec la liaison, écrivez[style.width.px], ou{ width: '90px' }. - Objet modifié sur place: après
etats['alerte'] = true,NgClassvoit le changement (elle compare le contenu à chaquengDoCheck), la liaison[class]non : elle ne réagit qu'à un nouvel objet. Cliquez sur « etats = nouvel objet » : les deux colonnes affichent alorsgras alerte. - Avec les signals, on ne modifie jamais un objet sur place :
set(),update()etcomputed()produisent toujours une nouvelle valeur, et cette troisième différence disparaît.

- Autre piège :
[style.width]="200"(un nombre sans unité) est ignoré par le navigateur. Écrivez[style.width.px]="200".
-
Combiner plusieurs sources
- Les classes s'additionnent: classe statique,
[class],[class.x],[ngClass]et les liaisons d'hôte (host: { '[class.mode-urgent]': … }), qui s'appliquent à la balise du composant lui-même. - Pour une même propriété de style, deux sources se concurrencent. Ici
[ngStyle]l'emporte sur[style.color], quel que soit l'ordre des attributs : ne donnez qu'une source à chaque propriété.
- Les classes s'additionnent: classe statique,
import { Component, signal } from '@angular/core';
import { NgStyle } from '@angular/common';
// Classe statique, [class], [class.x] et liaisons d'hôte se combinent ; [ngStyle] et [style.x] se concurrencent
@Component({
selector: 'app-ensemble',
imports: [NgStyle],
template: `
<button (click)="urgent.set(!urgent())">Urgent : {{ urgent() ? 'oui' : 'non' }}</button>
<p #p class="boite" [class]="{ arrondi: true }" [class.urgent]="urgent()">Classes combinées</p>
<code>class="{{ p.className }}"</code>
<p #q [style.color]="'blue'" [ngStyle]="{ color: 'red' }">[style.color]="blue" + [ngStyle] color: red</p>
<code>style="{{ q.getAttribute('style') }}"</code>
`,
host: { '[class.mode-urgent]': 'urgent()' },
styles: `
:host { display: block; padding: 4px; } :host(.mode-urgent) { background: #fff7ed; }
.boite { padding: 6px 10px; border: 1px solid #cbd5e1; margin: 6px 0; } .arrondi { border-radius: 12px; }
.urgent { border-color: #ea580c; font-weight: 700; } code { color: #64748b; font-size: 12px; }
`,
})
export class Ensemble {
protected readonly urgent = signal(true);
}
![Classes combinées et conflit entre [style.color] et ngStyle](https://apcpedagogie.com/wp-content/uploads/2026/11/angular-exemple-ensemble.png)

-
Des variables CSS pour le thème
- Plutôt que de lier dix styles, on lie une variable CSS sur l'hôte du composant (
'[style.--accent]') ; la feuille de styles l'utilise partout avecvar(--accent), et tous les éléments enfants en héritent :
- Plutôt que de lier dix styles, on lie une variable CSS sur l'hôte du composant (
import { Component, signal } from '@angular/core';
// Une variable CSS liée par [style.--nom] : tout le composant suit la couleur choisie
@Component({
selector: 'app-variables-css',
template: `
@for (c of couleurs; track c) {
<button class="pastille" [style.background]="c" [class.choisie]="c === accent()" (click)="accent.set(c)" [attr.aria-label]="c"></button>
}
<div class="boite">
<h4>Cours Angular</h4>
<p>Les couleurs de cette carte viennent toutes de <code>--accent</code>.</p>
<button class="principal">S'inscrire</button>
</div>
`,
host: { '[style.--accent]': 'accent()' },
styles: `
.pastille { width: 26px; height: 26px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px #cbd5e1; margin-right: 6px; cursor: pointer; }
.choisie { box-shadow: 0 0 0 3px var(--accent); }
.boite { margin-top: 10px; border: 2px solid var(--accent); border-radius: 12px; padding: 8px 14px; }
h4 { margin: 4px 0; color: var(--accent); }
.principal { background: var(--accent); color: #fff; border: 0; border-radius: 8px; padding: 6px 14px; }
`,
})
export class VariablesCss {
protected readonly couleurs = ['#dd0031', '#3178c6', '#16a34a', '#9333ea'];
protected readonly accent = signal('#3178c6');
}


-
Migrer vers les liaisons
- Angular fournit deux commandes de migration. Elles ne convertissent que les cas sans risque (un objet littéral sans clé à plusieurs classes, un objet de styles) et laissent le reste intact. Voici un composant avant migration :
import { Component, signal } from '@angular/core';
import { NgClass, NgStyle } from '@angular/common';
@Component({
selector: 'app-root',
imports: [NgClass, NgStyle],
template: `
<p [ngClass]="{ actif: actif(), alerte: note() < 10 }">Objet</p>
<p [ngClass]="{ 'gras souligne': actif() }">Clé à deux classes</p>
<p [ngClass]="['carte', theme()]">Tableau</p>
<p [ngClass]="classes">Variable</p>
<p [ngStyle]="{ color: couleur(), 'font-weight': 'bold' }">Style</p>
<p [ngStyle]="{ 'width.px': largeur() }">Unité</p>
`,
})
export class App {
actif = signal(true);
note = signal(8);
theme = signal('sombre');
couleur = signal('teal');
largeur = signal(200);
classes = { a: true };
}


- Le résultat réel après les deux commandes :
import { Component, signal } from '@angular/core';
import { NgClass } from '@angular/common';
@Component({
selector: 'app-root',
imports: [NgClass],
template: `
<p [class]="{ actif: actif(), alerte: note() < 10 }">Objet</p>
<p [ngClass]="{ 'gras souligne': actif() }">Clé à deux classes</p>
<p [ngClass]="['carte', theme()]">Tableau</p>
<p [ngClass]="classes">Variable</p>
<p [style]="{ color: couleur(), 'font-weight': 'bold' }">Style</p>
<p [style.width.px]="largeur()">Unité</p>
`,
})
export class App {
actif = signal(true);
note = signal(8);
theme = signal('sombre');
couleur = signal('teal');
largeur = signal(200);
classes = { a: true };
}
- Les
[ngStyle]sont tous convertis : l'objet devient[style], la clé à suffixe devient[style.width.px], etNgStyledisparaît des imports. - Seul le premier
[ngClass]est converti. La clé'gras souligne'n'est migrée qu'avec l'option--migrate-space-separated-key, qui produit[class.gras]="actif()" [class.souligne]="actif()". Le tableau et la variable restent : c'est à vous de les convertir ([class]accepte aussi un tableau), puis de retirerNgClassdes imports.

-
Quelle écriture choisir ?
- Préférez une classe CSS à un style en ligne : la mise en forme reste dans la feuille de styles. Réservez
[style]aux valeurs réellement calculées (une largeur, une couleur choisie par l'utilisateur).
- Préférez une classe CSS à un style en ligne : la mise en forme reste dans la feuille de styles. Réservez

| Critère | [ngClass] / [ngStyle] | [class] / [style] |
|---|---|---|
| Import | NgClass, NgStyle |
Aucun |
| Statut en Angular 22 | Non dépréciées, migration proposée | Écriture recommandée |
| Clé à plusieurs classes | Oui ('a b': cond) |
Non : une clé par classe |
| Suffixe d'unité | Dans la clé de l'objet ('width.px') |
Dans le nom de la liaison ([style.width.px]) |
| Objet modifié sur place | Détecté (ngDoCheck) |
Non détecté : nouvel objet nécessaire |
| Coût | Comparaison du contenu à chaque vérification | Comparaison de la référence seulement |

-
Activité 1 : une barre de progression
- Créer le composant
Progressionavec deux entrées :libelleetvaleur(obligatoire). La valeur est arrondie et bornée entre 0 et 100 dans uncomputed. - La largeur de la barre est liée avec
[style.width.%], et sa classe vautfaible(moins de 30),moyen(moins de 70) oubon. Afficher trois compétences et un bouton « +10 % Angular ».
- Créer le composant

- Vérifier : Angular passe de rouge à orange à 30 %, et une valeur de 130 donne une barre pleine à 100 %.
- Pourquoi utiliser
[style.width.%]plutôt qu'une classe par largeur ?
-
Activité 2 : des badges de statut
- Créer le composant
Badge(entréesstatutetnouveau). Le statut vaut'brouillon' | 'publie' | 'archive' | 'refuse'. - Les classes sont posées sur l'hôte: une classe statique
badgeet une liaison'[class]': 'classes()', oùclassesest uncomputedqui renvoie{ [statut]: true, nouveau }. Le style de chaque statut est défini avec:host(.publie)…
- Créer le composant

- Vérifier : l'hôte du premier badge porte
class="badge nouveau publie". - La classe statique
badgedisparaît-elle quand[class]change ? Vérifiez dans les outils du navigateur.
-
Activité 3 : réserver des places de cinéma
- Afficher 18 sièges (rangées A à C, numéros 1 à 6) avec
@for. Cinq sont occupés : classeoccupeet bouton désactivé. - Un clic sur un siège libre le choisit ou le libère : les sièges choisis sont dans un signal
Set<string>, et le siège porte[class.choisi]. Afficher le nombre de places et le total (8 DT la place) ; le total est grisé quand aucune place n'est choisie.
- Afficher 18 sièges (rangées A à C, numéros 1 à 6) avec

- Vérifier : B3 et B4 choisis donnent « 2 place(s) · 16 DT » ; un clic sur A3 (occupé) ne fait rien.
- Pourquoi
basculer()crée-t-il un nouveauSetau lieu d'appeleradd()sur l'ensemble existant ?
-
Activité 4 : un thème clair et sombre
- Créer le composant
ThemeCarte: un signaltheme('clair' | 'sombre') et deux palettes de variables CSS (--fond,--texte,--accent). - Lier la palette choisie sur l'hôte avec
'[style]': 'variables()'(uncomputed), et ajouter la classesombre. Toutes les règles CSS du composant utilisentvar(--…).
- Créer le composant

- Vérifier : en mode sombre, l'hôte porte
style="--accent: #38bdf8; --fond: #0f172a; --texte: #e2e8f0;". - Combien de liaisons faudrait-il sans variables CSS ? Et pour ajouter un troisième thème ?
-
Activité 5 : migrer un tableau de notes
- Réécrire ce template avec
[class.x]et[style.width.px], sansNgClassniNgStyle. Comparer ensuite avec le résultat des deux commandes de migration :
- Réécrire ce template avec
@for (n of notes(); track n.matiere; let pair = $even) {
<tr [ngClass]="{ zebre: pair, echec: n.valeur < 10, excellent: n.valeur >= 16 }">
<td>{{ n.matiere }}</td>
<td><span class="jauge" [ngStyle]="{ 'width.px': n.valeur * 6 }"></span> {{ n.valeur }}</td>
</tr>
}

- Vérifier : l'affichage est identique avant et après, et le tableau
importsest vide. - Que produit
ngclass-to-classsur ce[ngClass]: trois[class.x]ou un[class]avec objet ?
-
Activité 6 : un aperçu de texte
- Trois cases à cocher (gras, italique, souligné), un curseur de taille (12 à 36), un sélecteur de couleur et une liste d'alignement pilotent des signals.
- Un
computedclassesrenvoie l'objet de classes, un autrestylesrenvoie{ 'font-size', color, 'text-align' }. Le paragraphe d'aperçu utilise[class]="classes()"et[style]="styles()", et une ligne affiche les classes actives.

- Vérifier : en cochant « Italique » et en centrant, l'aperçu porte
class="apercu gras souligne italique"ettext-align: center. - Pourquoi la valeur de
font-sizedoit-elle contenirpxdansstyles()?
-
Activité 7 : tester classes et styles
- Écrire
apparence.spec.ts:Progression: avecsetInput('valeur', 25), la largeur vaut25%et la classefaible; avec 130,100%etbon;Sieges: deux clics affichent « 2 place(s) · 16 DT », un troisième libère le siège, un siège occupé est désactivé ;Apercu: après modification des signals,classListetstyledu paragraphe sont corrects.
- Écrire
$ ng test --include src/app/act-apparence/apparence.spec.ts
✓ Activités : classes et styles > Progression borne la valeur et choisit la classe du niveau
✓ Activités : classes et styles > Sieges : un clic choisit ou libère un siège, un siège occupé est désactivé
✓ Activités : classes et styles > Apercu applique les classes et les styles calculés
Test Files 1 passed (1)
Tests 3 passed (3)
- Vérifier : 3 tests réussis.
- Pourquoi le test compare-t-il
style.coloràrgb(147, 51, 234)et non à#9333ea?
- Les corrigés des sept activités, testés avec Angular 22, sont fournis dans l'archive de la leçon (dossier
corriges/).
-
Erreurs fréquentes
| Message ou symptôme | Cause | Solution |
|---|---|---|
NG8002: Can't bind to 'ngClass' since it isn't a known property of 'p'. |
NgClass non importé |
Utiliser [class], ou importer NgClass |
NG8002: Can't bind to 'ngStyle' since it isn't a known property of 'p'. |
NgStyle non importé |
Utiliser [style], ou importer NgStyle |
[class]="{ 'gras souligne': ok }" n'ajoute aucune classe |
Clé à plusieurs classes non gérée par la liaison | Une clé par classe, ou [class.gras] et [class.souligne] |
[style]="{ 'width.px': 90 }" sans effet |
Suffixe d'unité non géré dans l'objet de [style] |
[style.width.px]="90" ou { width: '90px' } |
[style.width]="200" sans effet |
Valeur CSS sans unité | [style.width.px]="200" |
La classe ne change pas après obj['x'] = true |
Objet modifié sur place avec [class] |
Créer un nouvel objet (computed, update) |
| L'affichage ne suit pas une valeur modifiée | Champ ordinaire modifié hors événement dans un composant OnPush |
Utiliser un signal |
[style.color] ignoré |
[ngStyle] définit aussi color et l'emporte |
Une seule source par propriété |
| La classe est ajoutée mais rien ne change | Classe absente du CSS du composant (styles encapsulés) | Définir la classe dans les styles du composant, ou utiliser :host(.classe) pour l'hôte |
| Une classe statique disparaît | [attr.class] ou [className] remplace tout l'attribut |
Utiliser [class], qui conserve les classes statiques |
-
À retenir
[ngClass]accepte une chaîne, un tableau, unSetou un objet{ classe: condition };[ngStyle]un objet{ propriété: valeur }avec des suffixes d'unité.- Ces deux directives ne sont pas dépréciées, mais les liaisons
[class],[class.x],[style]et[style.x.unité]font le même travail sans import. - Avec les liaisons : une clé par classe, l'unité dans le nom de la liaison (ou dans la valeur), et toujours un nouvel objet pour signaler un changement.
- Un
computed()qui renvoie l'objet de classes ou de styles garde le template lisible. - Les classes s'additionnent ; pour une propriété de style, une seule source.
- Une variable CSS liée sur l'hôte (
[style.--accent]) pilote tout un thème. ngclass-to-classetngstyle-to-stylemigrent les cas sûrs ; le reste se convertit à la main.
Le CSS décrit l'apparence, le template décide quand l'appliquer : liez des classes plutôt que des styles, et des valeurs calculées plutôt que des objets modifiés.