Blazor Custom Elements en HTML ou en React
Introduction
Depuis la version 7.0 de .NET, Microsoft a amélioré et intégré la possibilité de créer des HTML Custom Elements. Ces éléments personnalisés offrent aux développeurs, un moyen de créer leurs propres éléments DOM à fonctionnalités complètes. Dans Blazor, cela permet de publier ces composants vers d’autres Frameworks SPA, tels qu’Angular ou React. Plus d’info dans la documentation de Microsoft.
Les composants personnalisés Blazor :
-
Utilisent les interfaces HTML standard pour mettre en œuvre des éléments HTML personnalisés.
-
Éliminent la nécessité de gérer manuellement l’état et le cycle de vie des composants Blazor en utilisant les API JavaScript.
-
Sont utiles pour introduire progressivement des composants Blazor dans des projets existants écrits dans d’autres frameworks SPA.
Remarquez que ces éléments personnalisés Blazor ne prennent pas en charge les Child Contents ou les Templated Contents.
Créer un Blazor Custom Element
Plusieurs étapes sont nécessaires pour créer un composant Blazor à enregistrer comment Custom Element.
-
Créer un projet Blazor WebAssembly en version .NET 7.0 (ou supérieure).
-
Ajouter la dépendance NuGet Microsoft.AspNetCore.Components.CustomElements.
-
Créer votre composant Blazor de manière classique, avec des attributs
[Parameter]
pour les propriétés à exposer.<h1>Counter</h1> <p>Current count: @Value</p> <button @onclick="IncrementCount">Click me</button> @code { [Parameter] public int Value { get; set; } = 0; [Parameter] public EventCallback<int> ValueChanged { get; set; } [Parameter] public int Increment { get; set; } = 10; private async Task IncrementCount() { Value += Increment; if (ValueChanged.HasDelegate) await ValueChanged.InvokeAsync(Value); } }
- Enregistrer votre composant en tant que Custom Element via la commande suivante (dans Program.cs).
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
- Publier votre projet afin de générer les fichiers WebAssembly (dossier _framework) et le fichier JavaScript CustomElements (dossier _content).
⚠️ Afin de respecter les spécifications HTML, les noms des balises HTML Custom Element doivent respecter la case Kebab.
Examples
- “my-counter” et “my-cool-counter” sont valides
- “mycounter”, “MY-COUNTER” et “MyCounter” sont invalides
Intégrer un composant Blazor dans une page HTML
Une simple page index.html peut maintenant héberger ce nouveau composant qui est connu sous le nom my-counter.
-
Dans la page principale, index.html, ajouter une référence vers le fichier blazor.webassembly.js qui est responsable de charger et de démarrer l’application Blazor WASM, ainsi que de charger le fichier CustomElements.lib.module.js.
<html> <head> <base href="/" /> <script src="_framework/blazor.webassembly.js"></script> </head> <body> <my-counter id="myCounter" value="7" increment="2"></my-counter> </body> </html>
-
Démarrer ce site web via un serveur web tel que Live Server (vous ne pouvez pas double-cliquer sur le fichier index.html depuis Windows Explorer).
-
Pour l’instant, Blazor ne semble pas supporter l’assignation de méthodes EventCallback via les attributs des Custom Elements. Par contre, il est tout à fait possible de définir une fonction JavaScript pour ces méthodes:
myCounter.valueChanged = ...
. Noter l’attribut autostart=false pour démarrer Blazor manuellement viaBlazor.start()
<html> <head> <base href="/" /> <script src="_framework/blazor.webassembly.js" autostart="false"></script> </head> <body> <my-counter id="myCounter" value="7" increment="2"></my-counter> <script> Blazor.start().then(() => { var myCounter = document.getElementById("myCounter"); myCounter.valueChanged = (x) => { alert("You clicked " + x); }; }); </script> </body> </html>
Intégrer un composant Blazor dans une application React
Avant toute chose, il est nécessaire de disposer d’une application React et d’y référencer le composant Blazor créé précédemment. Un exemple simple consiste à installer NodeJS et à exécuter les commandes suivantes.
- Créer une nouvelle application Hello World.
npx create-react-app hello-world
⚠️ Placer cette application un dossier au format kebab (en minuscules) pour éviter des problèmes de compilation.
- Se rendre dans le dossier de cette application et la démarrer pour voir si elle fonctionne correctement.
cd .\hello-world\ npm start
-
Copier les dossiers _content et _framework publiés précédemment, dans le dossier publique du site web React.
- Dans la page principale, index.html, ajouter une référence vers le fichier blazor.webassembly.js qui est responsable de charger et de démarrer l’application Blazor WASM. C’est ce fichier JS qui chargera le fichier CustomElements.lib.module.js tel que décrit dans le fichier blazor.boot.json. Pour que cette chaine de référencement fonctionne correctement, il est nécessaire d’ajouter la base
<base href="/" />
dans l’en-tête du fichier index.html.<base href="/" /> <script src="_framework/blazor.webassembly.js"></script>
- Il ne vous reste plus qu’à modifier l’application pour y ajouter le nouveau composant my-counter, dans le fichier App.js.
<div className="App"> <header className="App-header"> <my-counter increment="5"></my-counter> </header> </div>
- En démarrant cette nouvelle application (
npm start
), vous pouvez constater que le composant Blazor WebAssembly est bien présent et fonctionnel dans l’application React.
Propriétés EventCallback
Pour l’instant, Blazor ne semble pas supporter l’assignation de méthodes EventCallback via les attributs des Custom Elements.
<my-counter value-changed="myChangeHandler"></my-counter>
Par contre, il est tout à fait possible de définir une fonction JavaScript pour ces méthodes. Voici un exemple de composant React qui récupère la propriété onValueChanged
du composant React pour l’utiliser dans le composant HTML personnalisés.
import React from "react";
export default class Counter extends React.Component {
id = "myId";
onValueChanged = function (value) { };
render() {
return React.createElement('my-counter', { ...this.props, id: this.id });
}
componentDidMount() {
let element = document.getElementById(this.id);
element.valueChanged = (x) => {
this.props.onValueChanged(x);
};
}
}
<Counter onValueChanged="myChangeHandler"></Counter>