El two-way binding es la sincronización de un valor entre el código C# y un control de la interfaz en ambas direcciones.
En el artículo anterior vimos el one-way binding, donde los datos fluían del código al HTML. Ahora necesitamos recoger también lo que el usuario escribe, marca o selecciona.
Si usamos solo lo que sabemos hasta ahora, tendríamos que hacer esto para capturar lo que escribe el usuario:
<input value="@Nombre" @onchange="@(e => Nombre = e.Value.ToString())" />Tener que escribir un evento lambda para cada input sería una pesadilla de mantenimiento. Por suerte, Blazor nos ofrece el Two-Way Binding (Enlace Bidireccional) mediante la directiva @bind.
La directiva @bind
Cuando usamos @bind, le estamos diciendo a Blazor: “Mantén esta variable de C# y este input HTML perfectamente sincronizados. Si uno cambia, actualiza el otro.”
<h3>Editor de Perfil</h3>
<div class="mb-3">
<label>Nombre:</label>
<input class="form-control" @bind="UserName" />
</div>
<div class="mb-3">
<label>Edad:</label>
<input type="number" class="form-control" @bind="UserAge" />
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" @bind="IsActive" />
<label class="form-check-label">Usuario Activo</label>
</div>
<div class="mt-3">
<p>Hola, <strong>@UserName</strong>. Tienes @UserAge años.</p>
<p>Estado: @(IsActive ? "🟢 Activo" : "🔴 Inactivo")</p>
</div>
@code {
private string UserName = "Luis";
private int UserAge = 35;
private bool IsActive = true;
}Fíjate en la elegancia del sistema:
- No hemos tenido que convertir
stringaintmanualmente para la edad. Blazor lo hace por nosotros. - Si
UserAgecambia en el código, el input se actualiza. - Si el usuario cambia el input,
UserAgese actualiza.
Qué genera @bind
@bind es azúcar sintáctico alrededor del valor del control y su evento de cambio.
Cuando el compilador de Razor ve esto:
<input @bind="Texto" />Conceptualmente, genera algo parecido a esto (el código real incluye conversión y gestión de errores):
<input value="@Texto" @onchange="@((ChangeEventArgs __e) => Texto = __e.Value.ToString())" />Entender esto es importante para cuando necesitemos personalizar el comportamiento, como veremos ahora mismo.
Controlando el momento de actualización (@bind:event)
Por defecto, @bind actualiza la variable cuando el input pierde el foco (evento onchange).
Pruébalo: escribe en un input vinculado. Verás que el texto de abajo (<p>Hola @UserName</p>) no cambia letra a letra, sino solo cuando haces clic fuera o pulsas Tab.
¿Y si queremos una búsqueda en tiempo real? ¿O validación instantánea?
Necesitamos cambiar el evento disparador a oninput. Para eso usamos el modificador @bind
<h3>Buscador en tiempo real</h3>
<input class="form-control"
@bind="SearchTerm"
@bind:event="oninput"
placeholder="Escribe para buscar..." />
<p>Buscando: @SearchTerm</p>
@code {
private string SearchTerm { get; set; } = string.Empty;
}Ahora, la variable SearchTerm se actualiza con cada pulsación de tecla (keystroke).
Ojo con el rendimiento:
Usar oninput provoca una actualización con cada cambio. Si además lanzas búsquedas o trabajo costoso, aplica debounce o cancelación para no iniciar una operación por cada tecla.
Formateo de fechas con @bind:format
Trabajar con fechas en inputs (<input type="date">) suele ser un dolor de cabeza porque el navegador espera un formato específico (yyyy-MM-dd), independientemente de la cultura de tu usuario.
Blazor nos ayuda con el modificador @bind
<label>Fecha de Nacimiento:</label>
<input type="date"
@bind="BirthDate"
@bind:format="yyyy-MM-dd" />
<p>Naciste el: @BirthDate.ToShortDateString()</p>
@code {
private DateTime BirthDate = DateTime.Today;
}Al especificar el formato, Blazor se encarga de parsear la entrada del navegador y convertirla correctamente a tu objeto DateTime, y viceversa.
Listas desplegables
El binding funciona perfectamente con elementos <select>, tanto para selecciones simples como múltiples.
<select class="form-select" @bind="SelectedCity">
<option value="">-- Selecciona ciudad --</option>
@foreach (var city in Cities)
{
<option value="@city.Code">@city.Name</option>
}
</select>
<p>Has seleccionado el código: @SelectedCity</p>
@code {
private string SelectedCity = string.Empty;
// Clase simple City { Code, Name }
private List<City> Cities = [];
}Blazor es lo suficientemente listo para seleccionar el <option> correcto al cargar la página basándose en el valor inicial de SelectedCity.
Binding en componentes personalizados
Hasta ahora hemos hablado de elementos HTML. Pero, ¿podemos hacer Two-Way Binding entre dos componentes de Blazor? ¿Puede un padre “bindearse” a un dato de un hijo?
Sí, pero requiere seguir una convención de nombres estricta.
Si el hijo tiene un parámetro Value, debe tener un evento llamado ValueChanged.
Hijo (InputPersonalizado.razor):
<input value="@Value" @oninput="OnInputChanged" />
@code {
[Parameter] public string Value { get; set; } = string.Empty;
// Convención: NombreDelParametro + Changed
[Parameter] public EventCallback<string> ValueChanged { get; set; }
private async Task OnInputChanged(ChangeEventArgs e)
{
// Avisamos al padre del cambio
await ValueChanged.InvokeAsync(e.Value.ToString());
}
}Padre:
<InputPersonalizado @bind-Value="MiVariable" />