Initialisation des systèmes...

Baptiste.Dev
Retour aux notes
FrameworksIntermédiaireSérie : Blazor / Razor

Blazor : EventCallback

La communication enfant vers parent : EventCallback sans valeur, EventCallback<T> avec une valeur (int, objet, record pour plusieurs champs), et pourquoi EventCallback plutôt qu'Action ou Func.

Par Baptiste Vidal
4 min de lecture
Mis à jour il y a 2 semaines
blazorrazordotneteventcallbackcomposantsevenements

Blazor : EventCallback

En Blazor, un enfant ne remonte jamais l'information en modifiant son parent directement : il le prévient, et le parent décide quoi faire. Ce canal, c'est l'EventCallback, effleuré dans les composants. On le déplie ici.


# Un signal sans valeur

Sans donnée à transporter, EventCallback est un simple déclencheur. Le parent passe un handler, l'enfant l'invoque :

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

EventCallback est un struct, jamais null. Tu peux appeler InvokeAsync même si le parent n'a fourni aucun handler : rien ne se passe, sans exception. Pas besoin du if (OnClic != null) qu'imposerait un Action.

Si tu veux transmettre l'événement DOM brut, type le paramètre en EventCallback<MouseEventArgs> : tu peux alors câbler @onclick="OnClic" directement, sans lambda.


# Remonter une valeur typée

Le vrai intérêt : EventCallback<T> transporte une valeur de l'enfant vers le parent.

@* Notation.razor : l'utilisateur clique une étoile, on remonte la note *@
@for (int i = 1; i <= 5; i++)
{
    var valeur = i;   // capture : sinon la closure verrait i = 6 après la boucle
    <button class="etoile @(valeur <= Note ? "pleine" : "")"
            @onclick="() => NoteChoisie.InvokeAsync(valeur)">★</button>
}
 
@code {
    [Parameter] public int Note { get; set; }
    [Parameter] public EventCallback<int> NoteChoisie { get; set; }
}

Côté parent, le handler reçoit la valeur :

<Notation Note="note" NoteChoisie="EnregistrerNote" />
<p>Ta note : @note / 5</p>
 
@code {
    private int note;
    private void EnregistrerNote(int valeur) => note = valeur;
}

# Remonter un objet

Le T peut être n'importe quel type : ici, l'enfant remonte l'article cliqué.

@* ListeArticles.razor *@
@foreach (var article in Articles)
{
    <li @onclick="() => ArticleSelectionne.InvokeAsync(article)">@article.Nom</li>
}
 
@code {
    [Parameter] public IReadOnlyList<Article> Articles { get; set; } = [];
    [Parameter] public EventCallback<Article> ArticleSelectionne { get; set; }
}

# Remonter plusieurs valeurs

Un EventCallback<T> ne transporte qu'un seul T. Pour en remonter plusieurs, emballe-les dans un record (ou un tuple) :

public record TriDemande(string Colonne, bool Ascendant);
@code {
    [Parameter] public EventCallback<TriDemande> TriChange { get; set; }
 
    // On emballe les deux valeurs dans le record, puis on l'envoie
    private Task TrierParPrix() => TriChange.InvokeAsync(new("prix", true));
}
 
<button @onclick="TrierParPrix">Trier par prix</button>

# EventCallback vs Action / Func

On pourrait typer le paramètre en Action ou Func<Task>. Deux raisons de préférer EventCallback :

  • Re-render automatique : après l'appel, Blazor déclenche le rendu du parent tout seul. Avec un Action, le parent ne se rafraîchit pas et tu dois appeler StateHasChanged() à la main.
  • Awaitable : InvokeAsync accepte aussi bien un handler synchrone qu'async, et l'attend proprement. Un Action ne peut pas porter d'await.
@* Avec Action : le parent ne se re-rend PAS tout seul *@
<Enfant OnChose="() => { compteur++; StateHasChanged(); }" />
 
@* Avec EventCallback : le re-render est automatique *@
<Enfant OnChose="() => compteur++" />

C'est le mécanisme idiomatique de la communication enfant vers parent en Blazor.


# À retenir

  • Un enfant remonte l'info au parent via un EventCallback, jamais en le modifiant directement.
  • EventCallback<T> transporte une valeur : un int, un objet, ou un record pour plusieurs champs.
  • C'est un struct : InvokeAsync est sûr même sans handler côté parent (pas de null-check).
  • Préfère-le à Action / Func : re-render automatique du parent + awaitable.
  • Piège récurrent : dans une boucle for, capture la variable (var valeur = i;) avant de l'utiliser dans la closure.