Los componentes Input son controles Blazor que enlazan un valor y notifican sus cambios al contexto de edición del formulario.
¿Por qué? Porque un <input @bind="Edad"> estándar actualiza la variable, pero no se comunica con el EditContext. Si el usuario introduce un texto inválido, el EditForm no tiene una forma fácil de marcar ese campo específico como “erróneo” y aplicarle clases CSS de error.
Dentro de un EditForm, normalmente usaremos los controles que heredan de InputBase<T>.
Estos componentes hacen tres cosas:
Binding: Sincronizan el valor con @bind-Value.
Parsing: Convierten el string del HTML al tipo de dato de C# (int, decimal, DateTime, Enum).
Validación: Se suscriben al EditContext para saber si son válidos o no, y cambian su clase CSS automáticamente.
Vamos a conocer a los protagonistas.
InputText e InputTextArea
Son los más básicos. Se usan para propiedades de tipo string.
InputText: Renderiza un<input type="text">.InputTextArea: Renderiza un<textarea>.
<div class="mb-3">
<label>Título del artículo</label>
<InputText class="form-control" @bind-Value="Articulo.Titulo" />
</div>
<div class="mb-3">
<label>Contenido</label>
<InputTextArea class="form-control" @bind-Value="Articulo.Contenido" rows="5" />
</div>Fíjate que usamos @bind-Value (con V mayúscula) en lugar de @bind. Esta es la convención para componentes Blazor que exponen una propiedad Value explícita.
InputNumber
Este componente es importante para datos numéricos (int, double, decimal, float). Renderiza un <input type="number">.
La gran ventaja es que, si el usuario escribe algo que no es un número, Blazor gestiona el error de conversión internamente y marca el campo como inválido, en lugar de lanzar una excepción que rompa la aplicación.
<label>Precio (€)</label>
<InputNumber class="form-control" @bind-Value="Producto.Precio" />InputDate
Renderiza un <input> de fecha u hora. Admite tipos como DateTime, DateTimeOffset, DateOnly y TimeOnly, también en sus variantes anulables cuando corresponda.
Maneja automáticamente el formato local del navegador (yyyy-MM-dd), por lo que no tenemos que pelear con formatos de fecha.
<label>Fecha de lanzamiento</label>
<InputDate class="form-control" @bind-Value="Producto.FechaLanzamiento" />Con el parámetro Type puedes elegir valores como InputDateType.DateTimeLocal, InputDateType.Date, InputDateType.Month o InputDateType.Time.
InputCheckbox
Renderiza un <input type="checkbox"> y se vincula a propiedades bool.
<div class="form-check">
<InputCheckbox class="form-check-input" @bind-Value="Producto.EstaActivo" />
<label class="form-check-label">Producto disponible</label>
</div>InputSelect para listas desplegables
En HTML puro, un <select> siempre devuelve un string. Si tu modelo espera un int (ID de categoría) o un Enum (Tipo de Producto), en JavaScript tendrías que convertirlo manualmente.
InputSelect hace la conversión de tipos automáticamente.
Ejemplo con enteros
<label>Categoría</label>
<InputSelect class="form-select" @bind-Value="Producto.CategoriaId">
<option value="">-- Selecciona --</option>
@foreach (var cat in Categorias)
{
<option value="@cat.Id">@cat.Nombre</option>
}
</InputSelect>
@code {
// Si Producto.CategoriaId es int, Blazor convertirá el "value" del option a int.
}Ejemplo con enumeraciones
Esto es bastante cómodo. Blazor sabe iterar y parsear Enums.
public enum Colores { Rojo, Verde, Azul }
// En el modelo: public Colores ColorFavorito { get; set; }<label>Color</label>
<InputSelect class="form-select" @bind-Value="Modelo.ColorFavorito">
@foreach (var color in Enum.GetValues<Colores>())
{
<option value="@color">@color</option>
}
</InputSelect>InputRadioGroup para botones de opción
Los botones de opción (Radio Buttons) funcionan en grupo. Blazor gestiona esto con un componente contenedor InputRadioGroup y componentes hijos InputRadio.
<label>Nivel de Urgencia:</label>
<InputRadioGroup @bind-Value="Ticket.Urgencia">
<div class="form-check">
<InputRadio Value="1" class="form-check-input" />
<label>Baja</label>
</div>
<div class="form-check">
<InputRadio Value="2" class="form-check-input" />
<label>Media</label>
</div>
<div class="form-check">
<InputRadio Value="3" class="form-check-input" />
<label>Alta</label>
</div>
</InputRadioGroup>Lo genial es que el Value puede ser cualquier tipo (int, string, enum), y el RadioGroup se encargará de seleccionar el correcto.
Clases CSS automáticas
Una de las mejores características de usar estos componentes es que Blazor añade y quita clases CSS automáticamente según el estado del campo.
Según el estado del campo, el proveedor de clases de EditContext utiliza modified, valid e invalid. Por ejemplo, un campo editado con errores puede terminar como class="form-control modified invalid".
Esto nos permite dar feedback visual instantáneo solo con CSS, sin tocar C#.
/* app.css */
/* Borde verde si está modificado y es válido */
.modified.valid {
border: 2px solid green;
}
/* Borde rojo si es inválido (modificado o no) */
.invalid {
border: 2px solid red;
background-color: #ffe6e6;
}Ejemplo integrado
Vamos a juntar todo en un formulario de registro de empleado.
<EditForm Model="@Empleado" OnValidSubmit="Guardar">
<DataAnnotationsValidator />
<div class="row g-3">
<div class="col-md-6">
<label>Nombre</label>
<InputText @bind-Value="Empleado.Nombre" class="form-control" />
</div>
<div class="col-md-6">
<label>Edad</label>
<InputNumber @bind-Value="Empleado.Edad" class="form-control" />
</div>
<div class="col-12">
<label>Departamento</label>
<InputSelect @bind-Value="Empleado.Departamento" class="form-select">
<option value="IT">Tecnología</option>
<option value="HR">Recursos Humanos</option>
<option value="MK">Marketing</option>
</InputSelect>
</div>
<div class="col-12">
<div class="form-check">
<InputCheckbox @bind-Value="Empleado.EsJefe" class="form-check-input" />
<label class="form-check-label">Es Jefe de Equipo</label>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary mt-3">Guardar Empleado</button>
</EditForm>
@code {
public class EmpleadoModel
{
public string Nombre { get; set; } = string.Empty;
public int Edad { get; set; }
public string Departamento { get; set; } = "IT";
public bool EsJefe { get; set; }
}
private EmpleadoModel Empleado = new();
private void Guardar() { /* ... */ }
}