Blazor : le data binding
@bind en profondeur : événement de déclenchement, format et culture, @bind:get/set et @bind:after, et comment rendre un composant bindable avec la convention Value / ValueChanged.
Blazor : le data binding
@bind est apparu vite dans les composants. Derrière ce petit mot-clé se cache tout un système : événement de déclenchement, format, culture, callbacks, et liaison bidirectionnelle entre composants. On déplie.
# Ce que @bind fait vraiment
@bind="x" est du sucre syntaxique : le compilateur génère à la fois la lecture (value="@x") et l'écriture (@onchange qui réaffecte x). Ces deux écritures sont équivalentes :
<input @bind="nom" />
@* Ce que le compilateur génère, en substance : *@
<input value="@nom" @onchange="e => nom = e.Value?.ToString()" /><input @bind="nom" /> @* Ce que le compilateur génère, en substance : *@<input value="@nom" @onchange="e => nom = e.Value?.ToString()" />Une seule direction ne suffit donc pas à comprendre @bind : il lit la valeur pour l'afficher et l'écrit quand l'utilisateur la change.
# @bind:event : quand la valeur remonte
Par défaut, @bind écoute onchange : la valeur ne remonte qu'à la perte de focus. Pour réagir à chaque frappe, cible oninput :
<input @bind="recherche" @bind:event="oninput" />
<p>@recherche.Length caractères saisis</p><input @bind="recherche" @bind:event="oninput" /><p>@recherche.Length caractères saisis</p>onchange convient à un formulaire qu'on valide à la fin ; oninput à une recherche instantanée ou un compteur en direct.
# @bind:format et la culture
Pour les dates et les nombres, @bind:format contrôle la conversion vers la chaîne affichée :
<input type="date" @bind="dateNaissance" @bind:format="yyyy-MM-dd" /><input type="date" @bind="dateNaissance" @bind:format="yyyy-MM-dd" /> Piège culture : @bind parse et formate avec la culture courante du thread. En fr-FR, 1,5 est accepté mais 1.5 échoue. Pour un format déterministe (stockage, API), impose la culture avec @bind:culture="Culture" (un champ CultureInfo), typiquement CultureInfo.InvariantCulture.
# @bind:get / @bind:set : le contrôle total
Depuis .NET 7, tu peux dissocier lecture et écriture pour intercaler ta propre logique (validation, normalisation) :
<input @bind:get="nom" @bind:set="MettreAJourNom" />
@code {
private string nom = "";
private void MettreAJourNom(string valeur) => nom = valeur.Trim();
}<input @bind:get="nom" @bind:set="MettreAJourNom" /> @code { private string nom = ""; private void MettreAJourNom(string valeur) => nom = valeur.Trim();}Et @bind:after (.NET 8) déclenche une action après la mise à jour de la valeur, sans casser le binding :
<input @bind="filtre" @bind:after="Rechercher" />
@code {
private string filtre = "";
private async Task Rechercher() { /* relance la requête filtrée */ }
}<input @bind="filtre" @bind:after="Rechercher" /> @code { private string filtre = ""; private async Task Rechercher() { /* relance la requête filtrée */ }}# Rendre un composant bindable (@bind-Value)
Pour qu'un parent puisse écrire @bind-Value sur ton composant, respecte la convention Value + ValueChanged (et, pour la validation, ValueExpression) :
@* ChampTexte.razor *@
<input value="@Value"
@onchange="e => ValueChanged.InvokeAsync(e.Value?.ToString())" />
@code {
[Parameter] public string? Value { get; set; }
[Parameter] public EventCallback<string?> ValueChanged { get; set; }
}@* ChampTexte.razor *@<input value="@Value" @onchange="e => ValueChanged.InvokeAsync(e.Value?.ToString())" /> @code { [Parameter] public string? Value { get; set; } [Parameter] public EventCallback<string?> ValueChanged { get; set; }}@* Utilisation : liaison bidirectionnelle avec le parent *@
<ChampTexte @bind-Value="nom" />@* Utilisation : liaison bidirectionnelle avec le parent *@<ChampTexte @bind-Value="nom" />@bind-Value="nom" se déplie en Value="nom" plus un ValueChanged qui réaffecte nom. La paire Xxx / XxxChanged est la mécanique du binding entre composants.
# À retenir
@bind= sucre pour lecture (value) + écriture (@onchange).@bind:event="oninput"remonte la valeur à chaque frappe plutôt qu'à la perte de focus.@bind:formatet la culture régissent dates et nombres ; méfie-toi dufr-FRsur les décimaux.@bind:get/@bind:set(.NET 7) et@bind:after(.NET 8) donnent le contrôle total.- Un composant devient bindable via la convention
Value+ValueChanged(@bind-Value).
Prochaine étape : fournir des services à tes composants avec l'injection de dépendances.