CSS et Javascript isolation

Blazor offre la possibilité de réaliser des composants autonomes et facilement réutilisables notamment avec la diffusion en paquet NuGet. Cependant, il fallait jusqu’à présent ajouter le CSS dans la partie header du fichier _Host.cshtml ou index.html sous la forme :

<link href="_content/Mycomponent/my_component.css" rel="stylesheet" />

Il en va de même pour les scripts JavaScript qui devaient être ajoutés dans le body :

<script src="_content/ Mycomponent / my_component.js"></script>

Il fallait donc lors de la diffusion du composant préciser à l’utilisateur de bien penser à ajouter ces éléments. La sortie de .Net5 apporte une évolution significative permettant d’améliorer le côté autonome en supprimant cette obligation. Il s’agit de l’isolation CSS et JavaScript dont je vais vous parler dans cet article.

Isolation CSS

Implémentation

Pour commencer à utiliser l’isolation CSS, il faut créer un projet Blazor et utiliser .Net5. Vous pouvez indifféremment utiliser Blazor server ou Blazor webassembly. La mise en place de l’isolation CSS est extrêmement simple. Il suffit d’ajouter une feuille de style portant le même nom que le composant. Par exemple, si l’on souhaite ajouter un style à l’index n’ayant comme porté que l’index lui-même, il suffit d’ajouter le fichier Index.razor.css

Et pour modifier la balise h1, il suffit d’ajouter le css suivant :

h1 {
    color: blue;
}

On peut constater que le h1 de l’index est bien bleu.

Alors que le h1 du composant counter est resté noir.

Fonctionnement

Au moment de la génération, Blazor combine tous les styles dans un unique fichier CSS nommé myblazorcomponent.style.css où myblazorcomponent est bien sûr le nom de l’application.

Dans le cas d’un projet appelé BlazorCSSJsisolation, on aura le fichier BlazorCSSJsisolation.styles.css.

On peut d’ailleurs le retrouver en ouvrant la console de développeur.

Autre point important, si l’on analyse l’élément h1, on peut remarquer l’ajout d’un attribut.

Et dans le fichier de style, on retrouve ce même attribut.

C’est ainsi que Blazor peut isoler le rendu css pour chaque composant, chacun ayant son propre attribut généré.

Isolation CSS et composants enfants

Style différent

On peut se poser la question du fonctionnement avec les composants enfant. En effet, il n’est pas rare d’avoir des composants imbriqués dans les autres. Par exemple, lors de la création d’un projet Blazor, on a par défaut un composant SurveyPrompt. Maintenant que l’on a vu comment fonctionne l’isolation, on comprend facilement qu’il faut ajouter un fichier css pour le composant enfant si l’on souhaite un style différent et spécifique.

Style identique au parent

Par défaut, l’isolation CSS s’applique uniquement au composant auquel elle est liée. Ainsi dans le fichier Index.razor, le h1 en bleu s’applique à l’index, mais pas au composant SurveyPrompt qui est inclus dans la page. C’est le style général qui s’applique. Il est malgré tout possible de propager le style isolé aux composants enfants.

Il faut pour cela utiliser l’attribut ::deep dans le fichier de style.

::deep h1 {
    color: blue;
}

Il faut ensuite encapsuler les éléments parents et enfants dans une même balise (div par exemple). Un attribut sera alors ajouté sur la balise div et le style sera appliqué au parent et à l’enfant.

Migration d’un projet .Net Core 3.x vers .Net5

Si vous migrez depuis un projet en .Net Core 3.x vers .Net5, il est possible que l’isolation CSS ne fonctionne pas comme attendu.

En effet, lors de la création d’un projet en .Net5 directement, le fichier de style BlazorCSSJsisolation.styles.css est automatiquement ajouté dans le fichier _Host.cshtml ou index.html. Ce fichier est la clé du fonctionnement de l’isolation CSS. Cependant lors de la migration depuis un projet antérieur, ce fichier n’est pas ajouté. Il faudra donc manuellement inclure la ligne <link href="BlazorCSSJsisolation.styles.css" rel="stylesheet" /> dans le header afin de faire fonctionner l’isolation CSS.

Isolation Javascript

L’isolation JavaScript fonctionne un peu différemment de l’isolation CSS. Il n’est pas possible de faire comme pour le CSS en créant un fichier composant.razor.js. Il faut cette fois créer un fichier js contenant les scripts et le placer dans le dossier wwwroot.

Dans l’exemple ci-dessus, le fichier se trouve à la racine de wwwroot, mais il est tout à fait possible de créer un sous-dossier (wwwroot/js).

C’est dans ce fichier, tools.js (le nom est libre), qu’il faut écrire les différentes fonctions JavaScript qui seront ensuite appelées dans l’application. Prenons comme exemple une fonction simple qui affiche Hello World.

export function HelloWorld() {
    alert('Hello World!');
}

Il est important de préfixer la fonction par le mot clé export.

On va ensuite ajouter un bouton sur la page d’accueil qui servira de déclencheur pour cette fonction JavaScript (Il s’agit bien sûr d’un exemple, vous n’êtes pas obligé de lier la fonction à un bouton).

Dans le fichier Index.razor, ajoutez :

<button class="btn btn-primary">Appel du JS</button>

En l’état, le bouton ne fait pas grand-chose. On va donc maintenant aller créer la fonction associée que l’on va appeler pour déclencher le JavaScript.

La suite se passe dans la partie code de notre Index (pour ma part Index.razor.cs).

On va se servir de IJSRuntime et de JSObjectReference. IJSRuntime.InvokeAsync permet d’appeler la fonction importée et JSObjectReference permet de garder un objet JavaScript en mémoire en conservant une référence vers le module ES chargé.

On va donc injecter le service IJSRuntime.

  [Inject]
        IJSRuntime JSRuntime { get; set; }

Puis on crée une instance de IJSObjectReference.

private Task<IJSObjectReference> _module;
private Task<IJSObjectReference> Module => _module ??= JSRuntime.InvokeAsync<IJSObjectReference>("import", "./tools.js").AsTask();

Le premier argument est la fonction import qui permet d’importer un module JavaScript. Le deuxième argument est le chemin vers le fichier contenant les fonctions JavaScript à importer. La variable module contient maintenant notre module JavaScript.

On utilise par ailleurs le .AsTask() pour convertir la ValueTask en Task car elle peut être attendue plusieurs fois.

Il ne reste plus maintenant qu’à créer la fonction que l’on va associer à notre bouton dans laquelle on appelle la fonction qui nous intéresse.

protected async Task SayHello()
        {
            var module = await Module;
            await module.InvokeVoidAsync("HelloWorld");
        }

On modifie le bouton pour ajouter la fonction.

<button class="btn btn-primary" @onclick="@(async () => await SayHello())">Appel du JS</button>

Quand on clique, on a bien l’alerte JavaScript qui se déclenche.

Code source

Le code servant d’illustration pour cet article se trouve sur github (niou128/BlazorCSSJsisolation: demo for CSS and Javascript Isolation (github.com)).

Laisser un commentaire