Pour les classes et styles dynamiques, utilisez les bindings natifs [class] / [class.x] et [style] / [style.x]. Les directives ngClass et ngStyle existent toujours, mais le guide de style actuel recommande les bindings natifs, et la v21 a ajouté des migrations automatiques pour eux.
Classes
<!-- Une seule classe conditionnelle -->
<li [class.active]="isActive()">...</li>
<!-- Plusieurs classes : chaîne, tableau ou objet -->
<div [class]="'card elevated'"></div>
<div [class]="['card', size()]"></div>
<div [class]="{ card: true, selected: isSelected(), disabled: !enabled() }"></div>
<!-- Les classes statiques et celles du binding sont COMBINÉES, pas écrasées -->
<button class="btn" [class.btn-primary]="primary()">OK</button>Styles
<!-- Une propriété CSS, avec une unité -->
<div [style.width.px]="width()"></div>
<div [style.height.%]="progress()"></div>
<div [style.font-size.rem]="scale()"></div>
<!-- Variables CSS (custom properties) -->
<div [style.--accent]="brandColor()"></div>
<!-- Plusieurs styles à la fois : chaîne ou objet -->
<div [style]="{ 'background-color': bg(), opacity: dim() ? 0.5 : 1 }"></div>Pourquoi pas ngClass / ngStyle
<!-- ✓ Nouveau : binding natif, sans import, plus rapide -->
<div [class]="{ active: isActive(), 'is-big': size() === 'lg' }"></div>
<div [style.width.px]="width()"></div><!-- ✗ Ancien : nécessite l’import de NgClass / NgStyle (ou CommonModule) -->
<div [ngClass]="{ active: isActive, 'is-big': size === 'lg' }"></div>
<div [ngStyle]="{ 'width.px': width }"></div>- Aucun import : il fait partie du compilateur, ce n’est pas une directive.
- Plus rapide : aucun differ ne compare l’objet à chaque Change Detection.
- Plus lisible pour le cas le plus courant, une classe conditionnelle :
[class.active]="x()".
# v21 : migrations automatiques
ng generate @angular/core:ngclass-to-class
ng generate @angular/core:ngstyle-to-styleClasses sur l’élément hôte : host
Pour l’élément hôte d’un composant (la balise <app-button> elle-même), utilisez la propriété host des métadonnées, pas @HostBinding. Depuis la v21, ces bindings sont aussi vérifiés par le typage.
// Classes sur l’élément hôte du composant → les métadonnées `host`, pas @HostBinding
@Component({
selector: 'app-button',
host: {
class: 'ui-btn', // statique
'[class.is-loading]': 'loading()', // dynamique
'[class]': 'variant()', // 'primary' | 'ghost'
'[style.--hue]': 'hue()',
'[attr.aria-busy]': 'loading()',
},
template: `<ng-content />`,
})
export class Button {
readonly variant = input<'primary' | 'ghost'>('primary');
readonly loading = input(false, { transform: booleanAttribute });
readonly hue = input(330);
}Qui gagne quand plusieurs endroits définissent le même style
Un même élément peut recevoir un style de cinq endroits : le host de son propre composant, le host d’une directive, un attribut statique style, une map [style] et un binding [style.x]. Angular ne laisse pas gagner le dernier écrit — il applique un ordre de priorité fixe.
- écrasé
- écrasé
- écrasé
Le gagnant est lu dans le navigateur (getComputedStyle), pas calculé par le labo.
De haut en bas : un binding sur une seule propriété bat une map, une map bat une valeur statique — et tout ce qui est dans le template bat ce que les directives et le composant posent sur leur hôte. Même un attribut statique style dans le template l’emporte sur le host binding d’une directive. Le même ordre vaut pour les classes : [class.active]="false" retire un active écrit dans l’attribut statique class. Un détail de plus : un binding « désactivé » doit valoir undefined, qui laisse la décision à la couche du dessous. null signifie « supprime ce style » : passez-y et le fond disparaît, même avec toutes les autres couches actives.
// de la priorité la plus haute à la plus basse :
// 1. [style.background]="…" un property binding dans le template
// 2. [style]="{ background: … }" un map binding dans le template
// 3. style="background: …" une valeur statique dans le template
// 4. les host bindings d’une directive sur l’élément
// 5. les host bindings du composant lui-même
@Component({
template: `
<app-swatch
appTint
style="background: yellow"
[style]="{ background: 'green' }"
[style.background]="'red'"
/>
`,
})
export class Page {} // → rouge : le numéro 1 est actif
@Directive({ selector: '[appTint]', host: { '[style.background]': "'blue'" } })
export class Tint {}
@Component({ selector: 'app-swatch', host: { '[style.background]': "'gray'" }, template: '' })
export class Swatch {}Pièges fréquents
[class]et[class.x]sur le même élément fonctionnent ensemble ; ordre de priorité : le binding le plus précis ([class.x]) l’emporte.- Mutation de l’objet de classes : si vous modifiez le même objet sur place, Angular peut ne pas le remarquer. Créez-en un nouveau (ou utilisez un
computed()). - Unités oubliées :
[style.width]="200"ne fait rien ; écrivez[style.width.px]="200". - Styles encapsulés : les classes appliquées aux enfants projetés ne sont pas stylées par le CSS de votre composant (utilisez
:hostou des variables CSS). Le fonctionnement de l’encapsulation est montré en direct dans Styles des composants.