Appliquer des styles CSS aux vues Angular

Cet article fait partie de la série d’articles Angular from Scratch.

@_stfeyes

Angular permet d’appliquer un style CSS sur les éléments des vues des composants. Ce style CSS peut être défini de façon globale pour toute l’application, au niveau d’un composant ou plus spécifiquement à un élément.

Le but de cet article est d’expliciter différentes méthodes pour appliquer un style CSS aux éléments d’un composant. Il est possible d’appliquer un style en utilisant des fichiers Sass, less ou stylus avec les extensions respectivement .scss, .less ou .styl toutefois ces formats de feuille de style ne seront pas traités dans cet article.

Dans un premier temps, on va indiquer comment appliquer un style ou une classe CSS aux éléments d’une application Angular de façon globale, puis à l’échelle d’un composant.

Pour comprendre les différents exemples, on va créer une application Angular en utilisant le CLI Angular:

ng new css-test --skip-tests --no-routing --style css

Cette ligne permet de créer une application nommée css-test:

  • en évitant la génération des fichiers de tests (avec --skip-tests),
  • en évitant d’installer un module de routing (avec --no-routing) et
  • en précisant l’extension des fichiers de style CSS (avec --style css)

On supprime tout ce qui se trouve dans le template du composant principal (cf. csstest/src/app/app.component.html).

Où définir les styles CSS ?

Appliquer un style globalement

Pour définir et appliquer des styles globalement à l’échelle de l’application, il faut indiquer le fichier CSS contenant ces styles dans les paramètres du projet Angular dans angular.json au niveau du paramètre styles. Ce paramètre permet d’indiquer le chemin des fichiers de style utilisables dans l’application:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "csstest": {
      "projectType": "application",
      "schematics": {},
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/csstest",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "aot": true,
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.css"
            ],
            "scripts": []
          }
        }
      }
    }
  }
}

Par défaut le fichier src/styles.css existe et permet de définir des styles, toutefois on peut rajouter d’autres fichiers avec le paramètre styles dans angular.json.

Pour illustrer dans l’application d’exemple, on rajoute un composant nommé Parent en exécutant:

ng g c Parent

Avec l’implémentation suivante:

<h1>Composant parent</h1> 
<p>Contenu du composant parent</p> 

On modifie le composant principal:

<h1>Composant principal</h1> 
<div style="display: block; border: 1px solid black; padding: 10px; background-color: white;"> 
  <app-parent></app-parent> 
</div> 

<app-parent></app-parent> correspond au paramètre selector du composant parent. Pour plus de précisions sur ce paramètre, voir l’article Les vues des composants Angular.

On modifie le fichier css/src/styles.css pour préciser un style pour l’élément <h1>:

h1 { 
  color: blue; 
  font-family: arial,sans-serif; 
} 

.componenttitle { 
  text-transform: uppercase; 
} 

A l’exécution, on peut voir que le style défini pour <h1> est appliqué à l’élément dans le composant principal et dans le composant Parent, les éléments <h1> sont en bleu:

On peut aussi appliquer une classe définie dans un fichier global au niveau d’un composant. Par exemple si on modifie le template du composant Parent pour appliquer la classe componenttitle:

<h1 class="componenttitle">Composant parent</h1> 

La classe est prise en compte, à l’exécution, l’élément <h1> du composant Parent est en majuscules:

Si on affiche les outils développeurs du browser dans l’onglet “Editeur de styles” sous Firefox ou “Eléments” et “Styles” dans Chrome, on peut voir que la feuille de style styles.css contient les styles qui ont été définis pour toute l’application:

Affichage des outils de développement dans un browser

Pour afficher l’éditeur de styles:

  • Sous Firefox: on peut utiliser la raccourci [Maj] + [F7] ou en allant dans le menu “Outils” ⇒ “Développement web” ⇒ “Editeur de style”.
  • Sous Chrome: utiliser le raccourci [F12] (sous MacOS: [⌥] + [⌘] + [C], sous Linux: [Ctrl] + [Maj] + [I]) puis cliquer sur l’onglet “Elements”. A partir du menu, il faut aller dans “Afficher” ⇒ “Options pour les développeurs” ⇒ “Examiner les éléments”.

Appliquer un style sur les éléments d’un composant

Au lieu de définir des styles pour toute l’application, on peut les définir à l’échelle d’un composant seulement pour les éléments se trouvant dans ce composant.

Plusieurs méthodes sont possibles pour définir des styles dans un composant. Cette partie va détailler ces différentes méthodes.

Encapsulation des styles d’un composant

Par défaut, les styles définis dans un composant n’affecte que le composant dans lequel ils sont définis. Les styles des composants sont ainsi isolés et n’affectent pas les autres.

Pour illustrer cette partie, on va créer un 2e composant nommé other que l’on va placer sous le composant Parent en exécutant:

ng g c other

Et on modifie le template du composant principale (cf. src/app/app.component.html):

<h1>Composant principal</h1> 
<div style="display: block; border: 1px solid black; padding: 10px; margin-bottom: 10px; background-color: white;"> 
  <app-parent></app-parent> 
</div> 
<div style="display: block; border: 1px solid black; padding: 10px; margin-bottom: 10px; background-color: white;"> 
  <app-other></app-other> 
</div> 

L’implémentation du composant other est similaire à celle du composant Parent:

<h1>Composant other</h1> 
<p>Contenu du composant other</p> 

Si on modifie le contenu du fichier de style du composant Parent (cf. src/app/parent/parent.component.css):

p { 
  background-color: yellow; 
} 

On peut voir que seul le composant Parent est affecté par cette modification. Le contenu du composant other reste inchangé:

Ce comportement s’explique par le fait que le style des composants est isolé. Si on affiche l’onglet “Elements” des outils développeurs, on peut voir un code similaire au code suivant:

<app-root _nghost-thd-c19 ng-version="11.2.4"> 
  <h1 _ngcontent-thd-c19>Composant principal</h1> 
  <div _ngcontent-thd-c19 style="display: block; border: 1px solid black; padding: 10px; margin-bottom: 10px; background-color: white;"> 
    <app-parent _ngcontent-thd-c19 _nghost-thd-c16> 
      <h1 _ngcontent-thd-c16 class="componenttitle">Composant parent</h1> 
      <p _ngcontent-thd-c16>Contenu du composant parent</p> 
    </app-parent> 
  </div> 
  <div _ngcontent-thd-c19 style="display: block; border: 1px solid black; padding: 10px; margin-bottom: 10px; background-color: white;"> 
    <app-other _ngcontent-thd-c19 _nghost-thd-c18> 
      <h1 _ngcontent-thd-c18 >Composant other</h1> 
      <p _ngcontent-thd-c18>Contenu du composant other</p> 
    </app-other> 
  </div> 
</app-root> 

Ainsi des identifiants ont été rajoutés (parties en gras). Si on affiche le style de l’élément <p> en jaune du composant Parent, on peut voir:

p[_ngcontent-thd-c16] { 
  background-color: yellow; 
} 

Le style n’est pas défini de façon globale mais il est limité aux éléments contenant un attribut _ngcontent-thd-c16. Le seul élément <p> avec un attribut _ngcontent-thd-c16 est celui du composant Parent et non celui du composant other (qui est _ngcontent-thd-c18).

Ainsi, c’est en utilisant des attributs _nghost et _ngcontent spécifiques à un composant et en limitant la définition des styles aux éléments ayant certains attributs qu’Angular isole les styles pour le composant dans lequel ils ont été définis.

Comportement avec les composants enfants

L’encapsulation du style des vues implique que, par défaut, le style est strictement limité à la vue d’un composant. Le style défini dans un composant n’aura pas de conséquence sur des composants enfant et sur du contenu projeté. Seuls les éléments se trouvant dans le composant où le style est défini seront impactés.

Par exemple, si on considère 2 composants Parent et Child tels que Child est un composant enfant de Parent:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    @Component({
      selector: 'app-child', 
      template: './child.component.html'
    }) 
    export class ChildComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <app-child></app-child>
    Feuille CSS
    (parent.component.css)
    p {
      background-color: cadetblue; 
    } 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html', 
      styleUrls: [ './parent.component.css' ] 
    }) 
    export class ParentComponent { 
    } 

On peut voir que seul l’élément se trouvant dans le composant Parent est impacté par le style défini dans la feuille de style parent.component.css:

Si on modifie le code des composants de cette façon:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    @Component({
      selector: '[app-child]',
      template: './child.component.html'
    }) 
    export class ChildComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <p app-child></p> 
    Feuille CSS
    (parent.component.css)
    p {
      background-color: cadetblue; 
    } 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html', 
      styleUrls: [ './parent.component.css' ] 
    }) 
    export class ParentComponent { 
    } 

Le paramètre selector du composant Child est [app-child], ainsi pour afficher le composant Child dans la vue du composant Parent, il faut indiquer le selector sous forme d’attribut:

<p app-child></p> 

On peut voir que le style s’applique aussi à l’élément <p app-child></p>. On pourrait penser que le style défini dans le composant Parent s’applique au composant imbriqué Child toutefois ce n’est pas tout à fait le cas. En réalité, le style s’applique à l’élément hôte <p> du composant enfant qui se trouve dans le composant Parent:

Pour appliquer un style à des composants enfant, il faut utiliser le sélecteur :host ::ng-deep.

Définir les styles CSS

Paramètre styleUrls de @Component()

Par défaut, les styles CSS d’un composant sont définis dans un fichier séparé du template et de la classe du composant. Le chemin de ce fichier est indiqué en utilisant le paramètre styleUrls dans le décorateur @Component() du composant:

@Component({  
  selector: 'app-example',  
  templateUrl: './example.component.html',  
  styleUrls: ['./example.component.css']
})  
export class ExampleComponent {  
  constructor() { }  
} 

styleUrls est un tableau et permet d’indiquer plusieurs fichiers:

@Component({  
  selector: 'app-example',  
  templateUrl: './example.component.html',  
  styleUrls: ['./file1.css', './file2.css', './file3.css' ]
})  
export class ExampleComponent {  
  constructor() { }  
} 

Paramètre styles de @Component()

Utiliser un fichier séparé pour définir les styles CSS n’est pas obligatoire, on peut définir des styles directement dans la classe du composant en utilisant le paramètre styles du décorateur @Component(), par exemple:

@Component({  
  selector: 'app-example',  
  templateUrl: './example.component.html',  
  styles: [ 
    'div { display: block; border: 1px solid black; padding: 10px; margin-bottom: 10px; background-color: cadetblue; color: white }', 
    'h1 { text-transform: uppercase; color: blue }' 
  ]
})  
export class ExampleComponent {  
  constructor() { }  
} 

Le paramètre styles permet d’indiquer un tableau de façon à pouvoir définir plusieurs styles.

Avec le CLI Angular, on peut créer un composant directement avec le paramètre styles plutôt que le paramètre par défaut styleUrls en uilisant l’option --inline-style:

ng g c <nom du composant> --inline-style

Avec cette option, un fichier séparé contenant les styles ne sera pas créé.

Importer des fichiers de style

Comme pour les fichiers CSS classiques, il est possible d’importer des fichiers CSS dans les fichiers de styles des composants. L’intérêt est de profiter de l’encapsulation des vues de composant sans dupliquer le code des feuilles de style.

Pour importer un fichier CSS, il suffit d’indiquer le chemin relatif du fichier à importer avec la syntaxe suivante:

@import '<chemin relatif du fichier CSS à importer>'; 

Ou

@import url('<chemin relatif du fichier CSS à importer>'); 

Par exemple, pour importer un fichier CSS dans src/app/commonStyle.css dans le composant src/app/parent/parent.component.ts, il faut indiquer dans le fichier src/app/parent/parent.component.css:

@import url('../commonStyle.css'); 

L’intérêt est de pouvoir partager ce fichier pour plusieurs composants sans modifier le paramètre ViewEncapsulation et sans devoir dupliquer les styles.

Si le fichier à importer se trouve dans le même répertoire, il faut faire précéder le nom du fichier par './', par exemple:

@import './commonStyle.css'; 

Supprimer l’isolation des styles aux composants

On peut paramétrer un comportement différent concernant l’isolation des éléments de style définis dans un composant. Ce paramétrage se fait au niveau du paramètre encapsulation dans le décorateur @Component():

import { Component, ViewEncapsulation } from '@angular/core'; 

@Component({ 
  ... 
  encapsulation: ViewEncapsulation.Emulated 
}) 
export class CustomComponent { 
  ... 
} 

Les valeurs possibles de ViewEncapsulation sont:

  • ViewEncapsulation.Emulated (valeur par défaut): cette valeur isole les styles dans les composants par émulation. Angular effectue cette émulation en ajoutant des attributs _nghost et _ngcontent sur les éléments et des sélecteurs d’attributs sur les styles définis dans les composants. C’est le comportement décrit plus haut.
  • ViewEncapsulation.ShadowDom: avec ce paramètre les styles définis dans les composants sont aussi isolés dans le composant (comme pour ViewEncapsulation.Emulated). La différence est que ce n’est pas Angular qui va émuler ce comportement en rajoutant des attributs. Il va se servir de la fonctionnalité Shadow DOM du browser pour effectuer cette isolation.

    Ainsi pour utiliser la fonctionnalité Shadow DOM, Angular crée un Shadowing tree pour chaque composant de façon à séparer la structure et le style de ces composants du DOM.

    Si on reprend l’exemple précédent en affectant le paramètre ViewEncapsulation.ShadowDom au composant Parent, on peut voir qu’il n’y a plus de sélecteur de style pour l’élément <p> toutefois le style est toujours limité au composant Parent:

    p { 
      background-color: yellow; 
    } 
    
  • ViewEncapsulation.None: ce paramétrage consiste à ne pas isoler les styles au composant dans lesquels ils sont définis. En affectant ce paramètre sur un composant, le style sera défini de façon globale à toute l’application.

    Par exemple, si on considère 2 composants First et Second que l’on place directement dans le composant principal. Si on applique le paramètre ViewEncapsulation.None pour le composant First:

    • Template du composant principal (cf. src/app/app.component.html):
      <h1>Composant principal</h1> 
      <div style="display: block; border: 1px solid black; padding: 10px; margin-bottom: 10px; background-color: white;"> 
        <app-first></app-first> 
        <app-second></app-second> 
      </div>  
      
    • Composant First:
      Template
      (first.component.html)
      <h1>Composant First</h1>
      <p>Contenu composant Child</p> 
      Feuille CSS
      (first.component.css)
      p {
        background-color: yellow; 
      } 
      Classe
      (first.component.ts)
      @Component({
        selector: 'app-first', 
        template: './first.component.html', 
        styleUrls: [ './first.component.css' ], 
        encapsulation: ViewEncapsulation.None
      }) 
      export class FirstComponent { 
      } 
    • Composant Second:
      Template
      (second.component.html)
      <h1>Composant Second</h1>
      <p>Contenu composant Second</p>
      Classe
      (second.component.ts)
      @Component({
        selector: 'app-second', 
        template: './second.component.html'
      }) 
      export class SecondComponent { 
      } 

    On peut voir que les éléments <p> des composant First et Second sont affectés par le style (la propriété background-color des éléments <p> est yellow):

Si la vue d’un composant avec le paramètre encapsulation: ViewEncapsulation.None n’est pas affichée, le style défini dans ce composant ne sera pas appliqué de façon globale.

Créer directement des composants en précisant le paramètre encapsulation

Avec le CLI Angular, on peut créer des composants directement en précisant la valeur du paramètre encapsulation par exemple en exécutant la commande:

ng g c <nom du composant> --view-encapsulation None

Les valeurs possibles sont Emulated, None ou ShadowDom.

Comment appliquer des styles sur les éléments d’un composant ?

Plusieurs méthodes sont possibles pour définir et appliquer un style ou une classe CSS à un élément d’un composant:

  • Statiquement en définissant les styles ou en appliquant les classes CSS de façon inline directement dans l’élément en utilisant les attributs style et class.
  • Par attribute binding avec un attribut de type [attr.style] pour définir un style ou [attr.class] pour appliquer une classe CSS.
  • Par property binding avec un attribut de type [style] pour définir un style ou [class] pour appliquer une classe CSS.
  • En utilisant les objets ngStyle ou ngClass.

Appliquer un style ou une classe statiquement

Appliquer un style ou une classe sur un élément revient à utiliser les attributs style ou class directement comme pour une page HMTL classique, par exemple:

<div style="background-color: yellow"> 
  ... 
</div> 

Ou

<div class="cssClassName"> 
  ... 
</div> 

Si on prend l’exemple d’un composant dont l’implémentation est:

Template
(example.component.html)
<h1>Composant Example</h1>
<p>Contenu du composant example</p> 
Feuille CSS
(example.component.css)
.bluebackground {
  background-color: cadetblue; 
} 
 
.whitetext { 
  color: white; 
} 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html', 
  styleUrls: [ './example.component.css' ] 
}) 
export class ExampleComponent {} 

Appliquer un style est immédiat:

<h1>Composant Example</h1> 
<p style="background-color: cadetblue; color: white">Contenu du composant example</p> 

De même pour appliquer des classes:

<h1>Composant Example</h1> 
<p class="bluebackground whitetext">Contenu du composant example</p> 

Attribute binding

Pour appliquer des styles sur un élément par attribute binding, on peut utiliser directement l’attribut [attr.style] avec une chaîne de caractères contenant les styles:

<div [attr.style]="definedStyles"> 
  ... 
</div> 

definedStyles contient les styles sous forme de chaîne de caractères, par exemple:

background-color: cadetblue; color: white; 

De même pour appliquer des classes CSS, on peut utiliser l’attribut [attr.class] avec une chaîne de caractères contenant les classes à appliquer:

<div [attr.class]="definedClasses"> 
  ... 
</div> 

definedClasses contient les classes sous forme de chaîne de caractères, par exemple:

bluebackground whitetext 

Par exemple, une implémentation complête dans un composant pour appliquer un style par attribute binding serait du type:

Template
(example.component.html)
<h1>Composant Example</h1>
<div [attr.style]="definedStyles"> 
  Contenu du composant example 
</div> 

<button (click)="changeStyle()">Change style</button> 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html'
}) 
export class ExampleComponent { 
  definedStyles = ''; 

  changeStyle(): void { 
    if (this.definedStyles === '') { 
      this.definedStyles = 'background-color: cadetblue; color: white'; 
    } 
    else { 
      this.definedStyles = ''; 
    } 
  } 
} 

En cliquant sur le bouton, on peut voir que le style est ajouté dynamiquement sur l’élément <div>.

De même pour une classe CSS, on peut appliquer une classe par attribute binding en utilisant attr.class:

Template
(example.component.html)
<h1>Composant Example</h1>
<div [attr.class]="definedCssClasses"> 
  Contenu du composant example 
</div> 
 
<button (click)="changeCssClasses()">Change CSS classes</button> 
Feuille CSS
(example.component.css)
.bluebackground {
  background-color: cadetblue; 
} 

.whitetext { 
  color: white; 
}
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html', 
  styleUrls: [ './example.component.css' ] 
}) 
export class ExampleComponent { 
  definedCssClasses = ''; 

  changeCssClasses(): void { 
    if (this.definedCssClasses === '') { 
      this.definedCssClasses = 'bluebackground whitetext'; 
    } 
    else { 
      this.definedCssClasses = ''; 
    } 
  } 
} 

Property binding

On peut appliquer des styles ou des classes CSS sur des éléments du template en utilisant des property bindings. A la différence de l’attribute binding, le propery binding passe par l’intermédiaire d’une propriété Angular pour effectuer les changements dans le DOM (l’attribute binding intervient plus directement sur l’attribut d’un élément HTML).

Le property binding permet plus de flexibilité en permettant d’affecter des styles ou des classes CSS sous forme d’une chaîne de caractères, d’un tableau de chaînes de caractères, d’une expression ou d’object literal.

Utiliser des chaines de caractères

Pour affecter des styles sous forme de chaînes de caractères, la syntaxe est du type:

<div [style]="definedStyles"> 
  ... 
</div> 

definedStyles contient les styles sous forme de chaîne de caractères, par exemple:

background-color: cadetblue; color: white; 

De même pour appliquer des classes CSS, on peut utiliser l’attribut [class] avec une chaîne de caractères contenant les classes à appliquer:

<div [class]="definedClasses"> 
  ... 
</div> 

definedClasses contient les classes sous forme de chaîne de caractères, par exemple:

bluebackground whitetext 

Utiliser une expression

On peut utiliser directement une expression pour la valeur des attributs [style] et [class] pour affecter respectivement des styles ou une classe CSS:

<div [style]="<expression>"> 
  ... 
</div> 

Par exemple:

Template
(example.component.html)
<h1>Composant Example</h1>
<div [style]="backgroundColorStyle + ';' + whiteTextStyle"> 
  Contenu du composant example 
</div> 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html' 
}) 
export class ExampleComponent { 
  backgroundColorStyle = 'background-color: cadetblue'; 
  whiteTextStyle = 'color: white'; 
} 

A l’exécution, on peut voir que le style défini sous forme d’une expression "backgroundColorStyle + ';' + whiteTextStyle" est appliqué sur l’élément <div>.

Utiliser un object literal

On peut affecter les styles ou les classes CSS en utilisant des object literals. L’intérêt de ce type d’objet est de permettre de les paramétrer plus facilement dans la classe du composant.

L’object literal doit comporter:

  • des propriétés dont les noms correspondent aux propriétés du style et
  • des valeurs correspondant aux valeurs des styles à appliquer.
{ 
  <propriété de style 1>: '<valeur 1>', 
  <propriété de style 2>: '<valeur 2>', 
  ... 
  <propriété de style N>: '<valeur N>' 
} 

De même pour les classes CSS, l’object literal doit comporter:

  • des propriétés dont le nom correspond au nom de la classe CSS et
  • des valeurs sont forme de booléen. La valeur True indiquant que la classe doit s’appliquer.
{ 
  <nom de la classe CSS 1>: '<true ou false>', 
  <nom de la classe CSS 2>: '<true ou false>', 
  ... 
  <nom de la classe CSS N>: '<true ou false>' 
} 

Exemple pour affecter un style
Par exemple, le composant suivant permet d’affecter un style sous la forme d’un object literal à un élément <div>:

Template
(example.component.html)
<h1>Composant Example</h1>
<div [style]="styleAsObjectLiteral"> 
  Contenu du composant example 
</div> 
 
<button (click)="changeStyle()">Change style</button> 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html' 
}) 
export class ExampleComponent { 
  styleAsObjectLiteral = ExampleComponent.getDivStyle('', ''); 

  static getDivStyle(backgroundColorValue: string, textColorValue: string): { 
    backgroundColor: string, 
    color: string 
  } { 
    return { 
      backgroundColor: backgroundColorValue, 
      color: textColorValue 
    }; 
  } 

  changeStyle(): void { 
    if (this.styleAsObjectLiteral.backgroundColor === '') { 
      this.styleAsObjectLiteral = ExampleComponent.getDivStyle('cadetblue', 'white'); 
    } 
    else { 
      this.styleAsObjectLiteral = ExampleComponent.getDivStyle('', ''); 
    } 
  } 
} 

Pour que la détection de changement fonctionne et que le changement de valeur de l’object literal soit répercuté dans la vue gràce au property binding, il faut affecter l’object literal entièrement et non modifier seulement la valeur des propriétés.

Le changement de style ne sera pas répercuté dans la vue si on implémente la méthode changeStyle() de cette façon:

changeStyle(): void { 
    if (this.styleAsObjectLiteral.backgroundColor === '') {
      this.styleAsObjectLiteral.backgroundColor = 'cadetblue'; 
      this.styleAsObjectLiteral.color = 'white'; 
    } 
    else { 
      this.styleAsObjectLiteral.backgroundColor = ''; 
      this.styleAsObjectLiteral.color = ''; 
    }
  } 

Il faut utiliser le Camel case pour définir les propriétés de l’object literal

Dans cet exemple, l’object literal permet de définir la propriété background-color et color de l’élément <div>:

{ 
  backgroundColor: 'cadetblue', 
  color: 'white' 
} 

Cet exemple utilise le Camel case pour nommer les propriétés de l’object literal de façon à paramétrer les propriétés de l’élément HTML qui sont en Kebab case:

  • backgroundColor en Camel case permet de paramétrer le style background-color en Kebab case,
  • color permet de paramétrer le style color.

Exemple pour affecter une classe CSS
L’exemple affecte une classe CSS à un élément <div> en utilisant un object literal:

Template
(example.component.html)
<h1>Composant Example</h1>
<div [class]="cssClassesAsObjectLiteral"> 
  Contenu du composant example 
</div> 
 
<button (click)="changeCssClass()">Change class CSS</button> 
Feuille CSS
(example.component.css)
.bluebackground {
  background-color: cadetblue; 
} 

.whitetext { 
  color: white;  
} 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html',
  styleUrls: [ './example.component.css' ] 
}) 
export class ExampleComponent { 
  applyBlueBackgroundClass = false; 
  applyWhiteText = false; 
  cssClassesAsObjectLiteral = { 
  bluebackground: this.applyBlueBackgroundClass; 
  whitetext: this.applyWhiteText; 

  changeCssClass(): void { 
    this.applyBlueBackgroundClass = !this.applyBlueBackgroundClass; 
    this.applyWhiteText = !this.applyWhiteText; 

    this.cssClassesAsObjectLiteral = { 
      bluebackground: this.applyBlueBackgroundClass; 
      whitetext: this.applyWhiteText; 
    }; 
  } 
} 

Property binding sur une seule propriété du style

Au lieu d’effecter le style complètement, on peut modifier une propriété par property binding. Par exemple, pour appliquer des property bindings seulement sur les propriétés background-color et color d’un élément <div>:

<div [style.background-color]="<membre de type string dans la classe du composant>" 
     [style.color]="<membre de type string dans la classe du composant>"> 
  ... 
</div>  
Camel case vs Kebab case

Les propriétés de style peuvent être nommées de 2 façons en Camel case et en Kebab case:

  • En Kebab case: pour désigner la propriété de style background-color, on peut effectuer le property binding avec la propriété du même nom de l’objet style, par exemple
    <div [style.background-color]="<membre de type string dans la classe>"> 
      ... 
    </div> 
    
  • Camel case: pour désigner la propriété de style background-color, on peut effectuer le property binding avec la propriété backgroundColor de l’objet style, par exemple:
    <div [style.backgroundColor]="<membre de type string dans la classe>"> 
      ... 
    </div> 
    

L’exemple complet est:

Template
(example.component.html)
<div [style.background-color]="backgroundColorStyle" [style.color]="textStyle" >
  Contenu composant Example 
</div> 

<button (click)="changeStyle()">Change style</button> 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html' 
}) 
export class ExampleComponent { 
  backgroundColorStyle = ''; 
  textStyle = ''; 

  changeStyle(): void { 
    if (this.backgroundColorStyle === '') { 
      this.backgroundColorStyle = 'cadetblue'; 
    } 
    else { 
      this.backgroundColorStyle = ''; 
    } 

    if (this.textStyle === '') { 
      this.textStyle = 'white'; 
    } 
    else { 
      this.textStyle = ''; 
    } 
  } 
} 

Property binding sur une seule classe CSS

Au lieu d’affecter toutes les classes CSS en utilisant un object literal, on peut effectuer un property binding pour une seule classe CSS. Par exemple, pour indiquer avec un booléen si on veut appliquer les classes CSS bluebackground et whitetext sur un élément <div>, on peut effectuer des property bindings séparément:

<div [class.bluebackground]="<membre de type booléen dans la classe du composant>" 
     [class.whitetext]="<membre de type booléen dans la classe du composant>"> 
  ... 
</div>  

L’exemple complet est:

Template
(example.component.html)
<div [class.bluebackground]="applyBlueBackgroundClass" [class.whitetext]="applyWhiteTextClass" >
  Contenu composant Example 
</div> 

<button (click)="changeCssClasses()">Change CSS classes</button> 
Feuille CSS
(example.component.css)
.bluebackground {
  background-color: cadetblue; 
} 

.whitetext { 
  color: white;  
} 
Classe
(example.component.ts)
import { Component } from '@angular/core';

@Component({ 
  selector: 'app-example',  
  templateUrl: './example.component.html',
  styleUrls: [ './example.component.css' ]  
}) 

export class ExampleComponent { 
  applyBlueBackgroundClass = true; 
  applyWhiteTextClass = true; 
 
  changeCssClasses(): void { 
    this.applyBlueBackgroundClass = !this.applyBlueBackgroundClass; 
    this.applyWhiteTextClass =!this.applyWhiteTextClass;  
  } 
} 
[ngStyle] et [ngClass]

Quand on applique des property bindings avec des tableaux ou des object literals, on peut utiliser [ngStyle] et [ngClass] à place de, respectivement, [style] et [class] pour appliquer des styles ou une classe.

Sélecteurs Angular

Il existe des sélecteurs de style Angular qui permettent d’affiner les conditions d’application des styles et des classes. Après compilation, ces styles n’apparaissent plus dans le code de l’application.

Sélecteur :host

Le sélecteur :host peut être utilisé dans la déclaration des styles pour désigner l’élément hôte de la vue d’un composant.

Par exemple si on considère 2 composants appelés Parent et Child tels que Child est un composant enfant de Parent:

L’implémentation est:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    @Component({
      selector: 'app-child', 
      template: './child.component.html', 
      styleUrls: [ './child.component.css' ] 
    }) 
    export class ChildComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <app-child></app-child>
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html'
    }) 
    export class ParentComponent { 
    } 

Si on souhaite appliquer un style seulement au composant Child, on pourrait être tenté de définir le style CSS suivant dans le fichier child.component.css:

/* Ce code ne fonctionne pas */  
app-child { 
  display: block; 
  background-color: cadetblue; 
  border: 1px black solid; 
  font-family: Arial, sans-serif; 
} 

Cette définition ne fonctionne pas, pour appliquer un style sur le composant, il faut utiliser le sélecteur :host à la place de app-child:

:host { 
  display: block; 
  background-color: cadetblue; 
  border: 1px black solid; 
  font-family: Arial, sans-serif; 
  padding: 10px;
} 

Le résultat est:

Dans l’exemple précédent, le composant Child était directement l’élément affiché dans le composant Parent, le code HTML était du type:

<app-parent> 
  <h1>Composant Parent</h1> 
  <p>Contenu composant Parent</p> 
  <app-child> 
    <h1>Composant Child</h1> 
    <p>Contenu composant Child</p> 
  </app-child>
</app-parent> 

Au lieu d’être directement l’élément affiché, le composant enfant peut se trouver dans un élément hôte, par exemple si on définit le composant Child de cette façon:

Template
(child.component.html)
<h1>Composant Child</h1>
<p>Contenu composant Child</p> 
Classe
(child.component.ts)
@Component({
  selector: '[app-child]',
  template: './child.component.html', 
  styleUrls: [ './child.component.css' ] 
}) 
export class ChildComponent { 
} 

Pour l’afficher dans le composant Parent, on modifie le template de cette façon:

Composant Parent:

<h1>Composant Parent</h1> 
<p>Contenu composant Parent</p> 
<p app-child></p>

L’élément hôte de la vue du composant Child est désormais un élément <p>:

<app-parent> 
  <h1>Composant Parent</h1> 
  <p>Contenu composant Parent</p> 
  <p app-child> 
    <h1>Composant Child</h1> 
    <p>Contenu composant Child</p> 
  </p>
</app-parent> 

Le sélecteur :host s’applique à l’hôte du composant quel qu’il soit. Ainsi même si l’élément hôte a changé, le style est toujours appliqué. Le résultat est le même que précédemment.

Appliquer un style en appliquant une condition sur l’hôte

On peut restreindre l’application du style en appliquant une condition sur l’hôte du composant.

Par exemple si on veut appliquer le style si l’hôte est un élément <p>:

:host(p) { 
  ... 
} 

Si on veut appliquer le style si la classe redtext est appliquée sur l’hôte:

.host(.redtext) { 
  ... 
} 

On peut restreindre davantage les conditions en rajoutant d’autres conditions après :host(). Par exemple, si on veut appliquer un style:

  • Si l’hôte du composant est un élément <p>
  • Seulement sur les éléments <h1> se trouvant dans le composant

On définit le style de cette façon:

:host(p) h1 { 
  ... 
} 

Si considère les composants Parent et Child tels que Child est un composant enfant de Parent:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    @Component({
      selector: '[app-child]', 
      template: './child.component.html', 
      styleUrls: [ './child.component.css' ] 
    }) 
    export class ChildComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <p app-child></p> 
    <span app-child></span> 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html'
    }) 
    export class ParentComponent { 
    } 

Pour appliquer un style sur l’hôte du composant Child seulement si l’hôte est un élément <p>, on ajoute dans le fichier child.component.css:

:host(p) { 
  display: block; 
  background-color: cadetblue; 
  border: 1px black solid; 
  font-family: Arial, sans-serif; 
  padding: 10px;
} 

Dans cet exemple, on voit que le style n’est pas appliqué pour la ligne <span app-child></span>:

Sélecteur :host-context

Le sélecteur :host-context permet de désigner un antécédent du composant courant. On peut ainsi appliquer un style si un antécédent respecte une condition particulière.

Par exemple si on veut appliquer un style si un antécédent du composant est un élément <p>, on peut définir le style de cette façon:

:host-context(p) { 
  ... 
} 

Si on veut appliquer un style si la classe redtext est appliquée sur un antécédent du composant, on peut définir le style de cette façon:

:host-context(.redtext) { 
  ... 
} 

On peut restreindre l’application du style en rajoutant d’autres conditions après :host-context(). Par exemple, si on veut appliquer un style:

  • Si un antécédent du composant est un élément <p>
  • Seulement sur les éléments <h1> du composant.

On peut définir le style de cette façon:

:host-context(p) h1 { 
  ... 
} 

Si on considère 3 composants Parent, Middle et Child tels que:

  • Middle est un composant enfant de Parent et
  • Child est un composant enfant de Middle.

L’implémentation est:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    @Component({
      selector: '[app-child]', 
      template: './child.component.html', 
      styleUrls: [ './child.component.css' ] 
    })
    export class ChildComponent { 
    } 
  • Composant Middle:
    Template
    (middle.component.html)
    <h1>Composant Middle</h1>
    <p>Contenu composant Middle</p> 
    <span app-child></span>
    Classe
    (middle.component.ts)
    @Component({
      selector: '[app-middle]', 
      template: './middle.component.html', 
      styleUrls: [ './middle.component.css' ] 
    }) 
    export class MiddleComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <p app-middle></p>
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html', 
      styleUrls: [ './parent.component.css' ] 
    })
    export class ParentComponent { 
    } 

Pour appliquer un style sur l’hôte du composant Child seulement si un antécédent de Child est un élément <p>, on ajoute dans le fichier child.component.css:

:host-context(p) { 
  display: block; 
  background-color: cadetblue; 
  border: 1px black solid; 
  font-family: Arial, sans-serif; 
  padding: 10px;
} 

Le résultat est:

Dans cet exemple, même si l’hôte du composant Child est un élément span, le style est appliqué car l’hôte du composant Middle dans lequel se trouve Child est un élément <p>.

Pour appliquer un style seulement sur les éléments <h1> du composant Child si l’hôte d’un antécédent de ce composant est un élément <p> (dans child.component.css):

:host-context(p) h1 { 
  color: greenyellow; 
} 

Le résultat est:

Sélecteur ::ng-deep

La documentation indique que ce sélecteur doit être décomissioné dans les futures versions d’Angular toutefois dans la version 11, il est toujours supporté.

Il sert à appliquer un style ou une classe globalement et en particulier à des composants enfant. L’intérêt de ce sélecteur est d’appliquer un style sur des composants enfant pour lesquels on ne peut pas modifier le code.

Concernant la syntaxe, il faut placer ::ng-deep devant les autres sélecteurs de style nécessitant une application globale:

::ng-deep <autres sélecteurs de style> 

Par exemple:

  • ::ng-deep p {} permet d’appliquer un style sur tous les éléments <p> de l’application.
  • ::ng-deep .bluebackground {} permet d’appliquer un style à tous les éléments pour lesquels la classe CSS bluebackground est utilisée.
  • ::ng-deep p.bluebackground {} applique un style sur tous les éléments <p> utilisant la classe CSS bluebackground.
  • span ::ng-deep p {} applique le même style sur les éléments <span> du composant et sur tous les éléments <p> de l’application. Il faut placer ::ng-deep devant les sélecteurs pour lesquels on veut une application globale.

Les sélecteurs /deep/ et >>> ont les mêmes fonctions que ::ng-deep toutefois il est déconseillé de les utiliser. La syntaxe est la même:

/deep/ <autres sélecteurs de style> 

Ou

>>> <autres sélecteurs de style> 
::ng-deep doit être utilisé avec ViewEncapsulation.Emulated

Il faut utiliser le sélecteur ::ng-deep avec le paramètre ViewEncapsulation.Emulated. Cette valeur étant la valeur par défaut, il n’est pas obligatoire de la préciser.

Appliquer un style globalement

Si on utilise ::ng-deep seul, la définition d’un style ou d’une classe devient globale.

Par exemple, si considère les 3 composants First, Second et Parent tels que First et Second sont des composants enfant de Parent:

  • Composant First:
    Template
    (first.component.html)
    <h1>Composant First</h1>
    <p>Contenu composant First</p> 
    Classe
    (first.component.ts)
    @Component({
      selector: 'app-first', 
      template: './first.component.html', 
      styleUrls: [ './first.component.css' ] 
    }) 
    export class FirstComponent { 
    } 
  • Composant Second:
    Template
    (second.component.html)
    <h1>Composant Second</h1>
    <p>Contenu composant Second</p> 
    Classe
    (second.component.ts)
    @Component({
      selector: 'app-second', 
      template: './second.component.html', 
      styleUrls: [ './second.component.css' ] 
    }) 
    export class SecondComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <app-first></app-first> 
    <app-second></app-second> 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html', 
      styleUrls: [ './parent.component.css' ] 
    }) 
    export class ParentComponent { 
    } 

Si on définit le style suivant dans le fichier CSS du composant First (first.composant.css):

::ng-deep p { 
  background-color: cadetblue; 
} 

On peut voir que le style sera appliqué à tous les éléments <p>:

Appliquer un style à des composants enfant

Pour appliquer un style à des composants sans l’appliquer globalement, il faut que ::ng-deep soit précédé de :host:

:host ::ng-deep <autres sélecteurs de style> 

Par exemple, si on reprend l’exemple des composants Child, Middle et Parent tels que:

  • Child est un composant enfant de Middle et
  • Middle est un composant enfant de Parent.

L’implémentation est:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ys)
    @Component({
      selector: 'app-child', 
      template: './child.component.html', 
      styleUrls: [ './child.component.css' ] 
    }) 
    export class ChildComponent { 
    } 
  • Composant Middle:
    Template
    (middle.component.html)
    <h1>Composant Middle</h1>
    <p>Contenu composant Middle</p> 
    <app-child></app-child> 
    Classe
    (middle.component.ts)
    @Component({
      selector: 'app-middle', 
      template: './middle.component.html', 
      styleUrls: [ './middle.component.css' ] 
    }) 
    export class MiddleComponent { 
    } 
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <app-middle></app-middle> 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html', 
      styleUrls: [ './parent.component.css' ] 
    }) 
    export class ParentComponent { 
    } 

Si on définit le style suivant dans le fichier CSS du composant Middle (cf. Middle.composant.css):

:host ::ng-deep p { 
  background-color: cadetblue; 
} 

On peut voir que le style sera appliqué aux élément <p> de Middle et Child et non celui de Parent:

:host ::ng-deep est particulièrement utile si on veut personnaliser des éléments situés sur des composants pour lesquels on ne maitrise pas l’implémentation, par exemple des composants se trouvant dans @angular/material.

Appliquer un style dans la classe du composant

Jusqu’içi, on a indiqué des méthodes pour appliquer des styles à partir du template ou d’une feuille de style d’un composant. Il existe d’autres méthodes permettant d’appliquer un style par programmation dans le classe du composant en utilisant, par exemple, les décorateurs @HostBinding() ou @ViewChild().

Avec @HostBinding()

Le décorateur @HostBinding() permet d’effectuer un binding entre un attribut dans la classe d’un composant et une propriété de l’objet du DOM qui est l’hôte du composant. Par exemple, utiliser ce décorateur permet d’accéder directement à des propriétés de style ou de classe.

Par exemple, si on considère les composants Child et Parent tels Child est un composant enfant de Parent. L’implémentation est:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    import { Component, HostBinding } from '@angular/ core';
    
    @Component({  
      selector: '[app-child]', 
      template: './child.component.html'
    })  
    export class ChildComponent {  
      @HostBinding('style.background-color') backgroundColor: string = 'cadetblue';
    }  
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <p app-child></p> 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html'
    }) 
    export class ParentComponent { 
    } 

En utilisant @HostBinding() dans le composant Child, on peut modifier la propriété style.background-color pour appliquer à l’élément hôte du composant la couleur d’arrière plan. La vue du composant Child est affichée dans le composant Parent grâce à <p app-child></p>. Ainsi l’élément hôte de Child est <p>, par suite @HostBinding() s’applique à cet élément <p>:

L’implémentation est similaire pour appliquer une classe CSS. Il faut au préalable définir la classe dans la feuille CSS du composant Parent car l’encapsulation des styles limite la portée de la classe à ce composant seulement:

  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <p>Contenu composant Child</p> 
    Classe
    (child.component.ts)
    import { Component, HostBinding } from '@angular/ core';
     
    @Component({  
      selector: '[app-child]', 
      template: './child.component.html'
    })  
    export class ChildComponent {  
      @HostBinding('class.bluebackground') applyBlueBackground: boolean = true; 
    }  
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p>Contenu composant Parent</p> 
    <p app-child></p> 
    Feuille CSS
    (parent.component.css)
    .bluebackground {
      background-color: cadetblue; 
    } 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html', 
      styleUrls: [ './parent.component.css' ] 
    }) 
    export class ParentComponent { 
    } 

Le résultat est similaire à l’exemple précédent.

Avec ElementRef.nativeElement

On peut appliquer des styles ou des classes CSS en utilisant des objets de type ElementRef et Renderer2. Ces objets sont:

  • ElementRef: objet Angular permettant d’accéder à l’objet du DOM correspondant à un élément HTML grâce à la propriété ElementRef.nativeElement.
  • Renderer2: il s’agit d’un objet Angular permettant d’effectuer des modifications dans les objets du DOM.

La combinaison de ces 2 objets permet d’appliquer des éléments de style ou une classe CSS.

Par exemple, si on considère le composant suivant:

Template
(example.component.html)
<h1>Composant Example</h1>
<p #content>Contenu composant Example</p> 
Feuille CSS
(example.component.css)
.redtext {
  color: red; 
} 
Classe
(example.component.ts)
import { Component, OnInit, ViewChild, Renderer2 } from '@angular/core';

@Component({ 
  selector: 'app-example', 
  template: './example.component.html', 
  styleUrls: [ './example.component.css' ] 
}) 
export class ExampleComponent implements OnInit { 
  @ViewChild('content', { static: true }) contentElementRef !: ElementRef; 

  constructor(private renderer: Renderer2) {} 

  ngOnInit(): void { 
    this.renderer.setStyle(this.contentElementRef.nativeElement, 
      'background-color', 'cadetblue'); 

    this.renderer.setAttribute(this.contentElementRef.nativeElement, 
      'class', 'redtext'); 
  } 

}

Dans cet exemple, on utilise les éléments d’implémentation suivant:

  • On définit un élément <p> dans le template du composant et on l’identifie avec la variable référence #content. Cette variable référence va permettre d’atteindre cet élément dans la classe du composant.
  • Dans la classe du composant, on utilise le décorateur @ViewChild() pour requêter la vue et accéder à un élément qui y est définit. On identifie l’élément en utlisant la variable référence #content. L’option { static: true } permet d’indiquer qu’il faut effectuer la requête sur le contenu statique de la vue.

    @ViewChild() permet d’instancier un objet de type ElementRef correspondant à un objet wrappant l’objet du DOM correspondant à l’élément HTML <p> se trouvant dans la vue.

    Pour plus de détails sur @ViewChild() voir l’article Requêter les éléments d’une vue d’un composant Angular.

  • L’objet Renderer2 peut être initialisé pour le composant par injection de dépendances.

Grâce aux objets Renderer2 et ElementRef, on va:

  • Appliquer la couleur cadetblue à l’arrière plan de l’élément <p>:
    this.renderer.setStyle(this.contentElementRef.nativeElement, 'background-color', 'cadetblue');
    
  • Appliquer la classe CSS redtext au contenu de l’élément <p> (cette classe est définie dans la feuille de style du composant):
    this.renderer.setAttribute(this.contentElementRef.nativeElement, 'class', 'redtext');
    

Priorités d’application des styles et classes CSS

L’application des styles et des classes CSS se fait suivant un ordre particulier. Cet ordre s’applique de plusieurs façons:

  • Pour un élément à l’intérieur d’un composant: si plusieurs styles sont appliqués, la priorité s’exerce en considérant d’abord une définition spécifique:
    1. Les property bindings s’appliquant sur une propriété spécifique:
      [style.background-color]='backgroundColor'
      

      Et:

      [class.bluebackground]='canApplyBlueBackground'
      
    2. Les property bindings s’appliquant avec [style] ou [class]:
      [style]='appliedStyles'
      

      Et:

      [class]='appliedCssClasses'
      
    3. Les styles et classes appliqués statiquement:
      style='background-color: cadetblue'
      

      Et:

      class='redtext' 
      
  • Si des styles ou classes sont appliqués sur l’hôte d’un composant, la priorité s’exerce à partant du template du composant vers ce qui est définit à l’extérieur du composant:
    1. Bindings effectués sur l’hôte du composant
    2. Les host bindings effectués par une directive
    3. Les host bindings effectués par le composant

Priorités d’application des styles sur un élément

Si on applique des styles sur un élément de plusieurs façons, la priorité d’application des styles s’effectuera en priorité sur les styles définis par:

  1. Property binding sur une propriété spécifique,
  2. Property binding en utilisant [style],
  3. Les styles appliqués statiquement.

Par exemple, si on considère le composant suivant:

Template
(example.component.html)
<h1>Composant Example</h1>
<p [style.background-color]='greenBackgroundColor' 
   [style]='appliedStyles' 
   style='background-color: cadetblue'>
        1. Property binding sur une propriété spécifique
</p> 
<p [style]='appliedStyles' 
   style='background-color: cadetblue'>
        2. Property binding sur [style]
</p> 
<p style='background-color: cadetblue'>
        3. Style statique
</p>
Classe
(example.component.ts)
@Component({
  selector: 'app-example', 
  template: './example.component.html' 
}) 
export class ExampleComponent implements OnInit { 

  appliedStyles = 'background-color: yellow'; 
  greenBackgroundColor = 'lime'; 
} 

On peut voir la priorité d’application des styles:

Priorités d’application des styles sur l’hôte d’un composant

Si on applique des styles sur l’hôte d’un composant, la priorité d’application des styles s’effectuera en priorité sur les styles définis par:

  1. Bindings effectués sur l’hôte du composant
  2. Les host bindings effectués par une directive
  3. Les host bindings effectués par le composant

Par exemple, si on considère 2 composants Child et Parent tels que Child est un composant enfant de Parent ainsi que la directive applyGreenBackground. L’implémentation de ces éléments est:

  • La directive applyGreenBackground:
    import { Directive, HostBinding } from '@angular/core'; 
    
    @Directive({ 
      selector: '[applyGreenBackground]' 
    }) 
    export class ApplyGreenBackgroundDirective { 
      @HostBinding('style.background-color') backgroundColor: string = 'lime'; 
    } 
    
  • Composant Child:
    Template
    (child.component.html)
    <h1>Composant Child</h1>
    <ng-content></ng-content> 
    Classe
    (child.component.ts)
    import { Component, HostBinding } from '@angular/core';
    
    @Component({  
      selector: '[app-child]', 
      template: './child.component.html'  
    })  
    export class ChildComponent {  
      @HostBinding('style.background-color') backgroundColor: string = 'cadetblue'; 
    }  
  • Composant Parent:
    Template
    (parent.component.html)
    <h1>Composant Parent</h1>
    <p app-child applyGreenBackground [style]='appliedStyles'>
      1. Binding effectué dans le template
    </p> 
    <p app-child applyGreenBackground>
      2. Host binding effectué par la directive
    </p> 
    <p app-child>
      3. Host binding effectué par le composant
    </p> 
    Classe
    (parent.component.ts)
    @Component({
      selector: 'app-parent', 
      template: './parent.component.html'
    }) 
    export class ParentComponent { 
      appliedStyles = 'background-color: yellow'; 
    } 

Cet exemple montre 3 applications de styles par bindings sur le composant Child. Le template du composant Child contient <ng-content></ng-content> pour effectuer une projection de contenu (voir content projection pour plus de détails).

Les 3 exemples d’applications de styles sont:

  • Un binding effectué dans le template du composant Parent:
    <p app-child applyGreenBackground [style]='appliedStyles'>1. Binding effectué dans le template</p>
    

    Cet exemple montre que ce binding est le plus prioritaire.

  • Un host binding effectué par une directive:
    <p app-child applyGreenBackground>2. Host binding effectué par la directive</p> 
    

    La directive ApplyGreenBackgroundDirective modifie l’arrière-plan de son élément hôte avec le décorateur @HostBinding().

  • Un host binding efectué par le composant Child:
    <p app-child>3. Host binding effectué par le composant</p> 
    

    Le composant Child modifie l’arrière-plan de son élément hôte avec le décorateur @HostBinding().

On peut voir la priorité d’application des styles:

Pour résumer…

Dans une application Angular, on peut définir des styles ou des classes CSS de plusieurs façons:

  • Globalement pour toute l’application, au niveau d’un ou plusieurs fichiers indiqué dans angular.json au niveau du paramètre styles:
    
    {
      "projects": {
        "<nom du projet>": {
          "architect": {
            "build": {
              "options": {
                "styles": [
                  "src/styles.css"
                ]
              }
            }
          }
        }
      }
    }
    
  • Plus spécifiquement aux éléments affichés dans la vue d’un composant en définissant les styles ou les classes:
    • Dans des fichiers de style .css référencés au niveau du paramètre styleUrls du décorateur @Component() du composant:
      @Component({
          styleUrls: [ './<nom du composant>.component.css' ] 
      })
      
    • Directement dans la classe du composant en utilisant le paramètre style du décorateur @Component():
      @Component({
        styles: [ 
          '<styles CSS>' 
        ]
      })
      

Dans un fichier .css, il est possibe d’importer un autre fichier CSS en indiquant directement son chemin relatif:

@import url('<chemin relatif du fichier CSS à importer>'); 

Par défaut, les styles définis dans un composant sont limités aux éléments de la vue de ce composant. On peut supprimer cette isolation en utilisant le paramètre encapsulation du décorateur @Component():

@Component({ 
  ... 
  encapsulation: ViewEncapsulation.Emulated 
}) 

Les différentes valeurs possibles de ce paramètre sont:

  • ViewEncapsulation.Emulated (valeur par défaut): le style est isolé au composant. Angular effectue cette isolation en rajoutant des attributs _nghost et _ngcontent aux éléments HTML au moment de la compilation.
  • ViewEncapsulation.ShadowDom: le style est isolé au composant en utilisant la fonctionnalité Shadow DOM du browser.
  • ViewEncapsulation.None: le style n’est pas isolé au composant. Il sera appliqué à tous les éléments affichés à condition que la vue du composant soit affichée.

Appliquer un style ou une classe sur un élément d’un composant

Plusieurs méthodes sont possibles:

  • Statiquement en utilisant les attributs style et class sur l’élément du template:
    <div style="background-color: yellow"> 
      ... 
    </div>
    

    Ou

    <div class="cssClassName"> 
      ... 
    </div> 
    
  • Par attribute binding:
    <div [attr.style]="definedStyles"> 
      ... 
    </div>
    

    definedStyles est un membre de la classe contenant la chaîne de caractères avec le style.
    Ou

    <div [attr.class]="definedClasses"> 
      ... 
    </div>
    

    definedClasses contient les classes sous forme de chaînes de caractères séparées par des espaces.

  • Par Property binding:
    <div [style]="definedStyles"> 
      ... 
    </div> 
    

    definedStyles peut être:

    • Une chaîne de caractères contenant les styles,
    • Un tableau de chaînes de caractères contenant les styles,
    • Une expression,
    • Un object literal dont le nom des propriétés correspond au nom du style à appliquer. Le nom de la propriété doit être en Camel case.

    Ou

    <div [class]="definedClasses"> 
      ... 
    </div>
    

    definedClasses peut être:

    • Une chaîne de caractères contenant les noms de classe séparés par un espace,
    • Un tableau de chaînes de caractères contenant les noms de classe,
    • Un object literal dont le nom des propriétés correspond au nom de la classe, la valeur étant un booléan (True indique que la classe doit être appliquée, False indique que la classe ne doit pas être appliquée).

    On peut appliquer une seule propriété de style en utilisant les syntaxes:

    • En Kebab case:
      <div [style.background-color]="<membre de type string dans la classe du composant>"> 
        ... 
      </div>
      
    • En Camel case:
      <div [style.backgroundColor]="<membre de type string dans la classe du composant>"> 
        ... 
      </div>
      

    De même on peut appliquer une seule classe avec la syntaxe:

    <div [class.bluebackground]="<membre de type booléen dans la classe du composant>" 
      ... 
    </div>  
    

Sélecteurs Angular

Angular permet d’utiliser les sélecteurs spécifiques pour affiner l’application des styles:

  • :host applique un style à l’élément hôte de la vue d’un composant:
    :host { 
      /* Styles CSS */
    } 
    

    Pour appliquer le style si le type de l’hôte est <p>:

    :host(p) { 
      /* Styles CSS */
    } 
    

    Pour appliquer le style si la classe redtest est appliquée à l’hôte:

    .host(.redtext) { 
      /* Styles CSS */
    } 
    

    Pour appliquer le style si l’hôte est <p> sur les éléments <h1> du composant:

    :host(p) h1 { 
      /* Styles CSS */
    }
    
  • :host-context permet d’appliquer un style suivant une condition sur un antécédent du composant:
    :host-context(p) { 
      /* Styles CSS */
    }
    

    Ce style est appliqué si un antécédent de la vue du composant est un élément <p>.

    Pour appliquer un style si la classe redtest est appliquée à un antécédent du composant:

    :host-context(.redtext) { 
      /* Styles CSS */
    } 
    

    Pour appliquer un style si un antécédent est un élément <p> en limitant aux éléments <h1> de la vue du composant:

    :host-context(p) h1 { 
      /* Styles CSS */
    } 
    
  • ::ng-deep permet d’appliquer un style ou une classe globalement. Le sélecteur ::ng-deep doit être utilisé avec le paramètre ViewEncapsulation.Emulated. Il faut placer ::ng-deep devant les sélecteurs pour lesquels on veut une application globale:
    ::ng-deep p {
      /* Styles CSS */
    }
    

    Cette définition applique le style sur tous les éléments <p>.

    Pour appliquer un style si la classe bluebackground est appliquée sur un élément:

    ::ng-deep .bluebackground {
      /* Styles CSS */
    }
    

    Pour appliquer un style sur les éléments <p> sur lesquels la classe bluebackground est appliquée:

    ::ng-deep p.bluebackground {
      /* Styles CSS */
    }
    

    Dans l’exemple suivant, le style est appliquée spécifiquement aux éléments <span> du composant et globalement à tous les éléments <p>:

    span ::ng-deep p {
      /* Styles CSS */
    }
    
  • :host ::ng-deep permet d’appliquer un style aux composants enfant:
    :host ::ng-deep p {
      /* Styles CSS */
    }
    

    Le style sera appliqué aux éléments <p> du composant et de ses enfants.

Appliquer un style dans la classe d’un composant

  • @HostBinding(): ce décorateur effectue un binding entre un membre de la classe et une propriété de l’objet du DOM qui est l’hôte du composant.
    export class ExampleComponent {  
      @HostBinding('style.background-color') backgroundColor: string = 'cadetblue';
    } 
    

    Cette implémentation permet d’appliquer la couleur cadetblue à la propriété background-color de l’élément hôte du composant Example.

    export class ExampleComponent {  
      @HostBinding('class.bluebackground') applyBlueBackground: boolean = true; 
    } 
    

    Cet exemple permet d’appliquer le classe bluebackground à l’élément hôte du composant Example.

  • ElementRef.nativeElement: permet de s’interfacer avec l’objet du DOM d’un élément affiché sur la vue d’un composant. On peut appliquer un style ou une classe en passant par cet objet, par exemple:
    export class ExampleComponent implements OnInit { 
      @ViewChild('content', { static: true }) contentElementRef !: ElementRef; 
    
      constructor(private renderer: Renderer2) {} 
    
      ngOnInit(): void { 
        this.renderer.setStyle(this.contentElementRef.nativeElement, 
          'background-color', 'cadetblue'); 
    
        this.renderer.setAttribute(this.contentElementRef.nativeElement, 
          'class', 'redtext'); 
      } 
    }
    

    Dans cet exemple:

    • On effectue une requête sur la vue du composant pour récupérer un objet identifié par une variable référence #content.
    • On affecte la couleur cadetblue à la propriété de style background-color de l’élément requêté.
    • On applique la classe redtext à cet élément requêté.

Priorités d’application des styles sur un élément

Si des styles sont appliqués de façon concurrente sur un élément, la priorité d’application de ces styles se fait dans cet ordre:

  1. Property binding sur une propriété spécifique,
  2. Property binding en utilisant [style],
  3. Les styles appliqués statiquement.

Priorités d’application des styles sur l’hôte d’un composant

Si des styles sont appliqués de façon concurrente sur l’hôte d’un composant, la prorité d’application de ces styles se fait dans cet ordre:

  1. Bindings effectués sur l’hôte du composant
  2. Les host bindings effectués par une directive
  3. Les host bindings effectués par le composant

Leave a Reply