Initialisation des systèmes...

Baptiste.Dev
Retour aux notes
FrameworksDébutantSérie : Blazor / Razor

Blazor : les composants

Le composant .razor et son passage de paramètres : types variés, attribute splatting, paramètres en cascade, data binding, et un aperçu de EventCallback pour la communication enfant vers parent.

Par Baptiste Vidal
5 min de lecture
Mis à jour il y a 2 semaines
blazorrazordotnetcomposantsparametreseventcallback

Blazor : les composants

Un composant Blazor, c'est l'équivalent .NET d'un composant React : une brique d'interface réutilisable. Techniquement, un fichier .razor est compilé en une classe qui hérite de ComponentBase. On repart de la syntaxe Razor.


# Un composant = un fichier .razor

Un fichier Salutation.razor combine le markup (Razor) et la logique (@code). Son nom, en PascalCase, devient une balise.

@* Salutation.razor *@
<h1>Bonjour @Nom !</h1>
 
@code {
    [Parameter] public string Nom { get; set; } = "";
}

Utilisation, exactement comme une balise HTML :

<Salutation Nom="Baptiste" />
<Salutation Nom="Stefano" />

# Passer des paramètres

Pour qu'un composant s'adapte, on lui passe des données via des propriétés marquées [Parameter], l'équivalent des props de React. Un paramètre accepte n'importe quel type, pas seulement des chaînes :

@* CarteProduit.razor *@
<article class="carte @(EnPromo ? "promo" : "")">
    <h2>@Produit.Nom</h2>
    <p>@Produit.Prix.ToString("C")</p>
    <ul>
        @foreach (var tag in Tags)
        {
            <li>@tag</li>
        }
    </ul>
</article>
 
@code {
    [Parameter, EditorRequired] public Produit Produit { get; set; } = default!;
    [Parameter] public IReadOnlyList<string> Tags { get; set; } = [];
    [Parameter] public bool EnPromo { get; set; }
}

On lui passe un objet, une liste et un booléen :

<CarteProduit Produit="monProduit" Tags="etiquettes" EnPromo="true" />

Ce qu'il faut retenir sur le passage :

  • Un paramètre accepte tout type : string, int, record, collection, enum, délégué.
  • Les booléens sont explicites : EnPromo="true". Il n'y a pas de raccourci HTML <CarteProduit EnPromo>.
  • [EditorRequired] fait avertir l'éditeur quand un paramètre obligatoire est oublié.
  • Les paramètres sont en lecture seule côté enfant : le parent les réécrit à chaque rendu.

# Attribute splatting (@attributes)

Pour laisser passer des attributs HTML arbitraires (class, id, data-*, aria-*, @onclick) jusqu'à l'élément racine, capture-les puis déverse-les avec @attributes :

@* Bouton.razor *@
<button class="btn" @attributes="Extra">@ChildContent</button>
 
@code {
    [Parameter] public RenderFragment? ChildContent { get; set; }
 
    [Parameter(CaptureUnmatchedValues = true)]
    public IReadOnlyDictionary<string, object>? Extra { get; set; }
}
@* id, data-role et @onclick ne sont pas déclarés : ils atterrissent sur le <button> *@
<Bouton id="valider" data-role="submit" @onclick="Valider">Valider</Bouton>

CaptureUnmatchedValues = true récupère tout ce que le composant ne déclare pas. Indispensable pour des composants d'UI génériques (boutons, champs) qui doivent rester ouverts aux attributs standards.


# Passer un paramètre en cascade

Quand une donnée doit descendre à travers plusieurs niveaux de composants, la threader à chaque étage est pénible. CascadingValue la rend disponible à tous les descendants, sans la passer explicitement :

@* Layout.razor : on diffuse le thème vers tout le sous-arbre *@
<CascadingValue Value="theme">
    @Body
</CascadingValue>
 
@code {
    private string theme = "sombre";
}
@* N'importe quel composant en dessous, même profond, le reçoit *@
@code {
    [CascadingParameter] private string Theme { get; set; } = "";
}

C'est l'équivalent du Context de React. À réserver aux données transverses et stables (thème, utilisateur connecté, culture) ; pour un passage local, un [Parameter] classique reste plus lisible.


# Le data binding (@bind)

Un raccourci fréquent : lier un champ à une variable dans les deux sens.

<input @bind="nom" @bind:event="oninput" />
<p>Bonjour @nom</p>
 
@code { private string nom = ""; }

@bind génère la lecture et l'écriture. Le sujet est vaste (événement, format, culture, binding entre composants) : il a sa page dédiée, le data binding.


# Remonter au parent : EventCallback

Un enfant ne modifie pas le parent directement : il le prévient via un EventCallback. Sans valeur, c'est un simple signal :

@* Bouton.razor *@
<button @onclick="() => OnClic.InvokeAsync()">@Libelle</button>
 
@code {
    [Parameter] public string Libelle { get; set; } = "";
    [Parameter] public EventCallback OnClic { get; set; }
}

Un EventCallback<T> peut aussi transporter une valeur (la note choisie, l'article cliqué, un record de plusieurs champs) et redéclenche le rendu du parent. Le détail, les exemples et le choix face à Action sont sur sa page dédiée : EventCallback.


# La composition (RenderFragment)

Un composant peut recevoir du contenu balisé via ChildContent, de type RenderFragment :

@* Carte.razor *@
<div class="carte">@ChildContent</div>
 
@code {
    [Parameter] public RenderFragment? ChildContent { get; set; }
}
<Carte>
    <h2>Titre</h2>
    <p>N'importe quel balisage passe ici.</p>
</Carte>

Pour aller plus loin, un RenderFragment<T> permet au parent de fournir un gabarit paramétré (chaque élément d'une liste, par exemple) via la variable implicite @context. Comme React, Blazor privilégie la composition plutôt que l'héritage.


# À retenir

  • [Parameter] accepte tout type (objet, collection, enum) ; les booléens sont explicites ; [EditorRequired] marque les obligatoires.
  • @attributes + CaptureUnmatchedValues transmettent les attributs HTML non déclarés au composant.
  • CascadingValue / [CascadingParameter] diffusent une donnée transverse à tous les descendants.
  • L'enfant remonte au parent via EventCallback (une valeur avec EventCallback<T>) ; détails et exemples sur sa page dédiée.

Prochaine étape : où tout ce code s'exécute-t-il réellement ? L'hébergement et les modes de rendu.