Persiltech.Validation.Blazor 0.1.0

dotnet add package Persiltech.Validation.Blazor --version 0.1.0
                    
NuGet\Install-Package Persiltech.Validation.Blazor -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Persiltech.Validation.Blazor" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Persiltech.Validation.Blazor" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Persiltech.Validation.Blazor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Persiltech.Validation.Blazor --version 0.1.0
                    
#r "nuget: Persiltech.Validation.Blazor, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Persiltech.Validation.Blazor@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Persiltech.Validation.Blazor&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Persiltech.Validation.Blazor&version=0.1.0
                    
Install as a Cake Tool

Persiltech.Validation.Blazor

NuGet License: MIT Sponsor

Lleva los errores que devuelve una API al campo que los provocó. Los escribe en el EditContext de Blazor, así que el componente de entrada los pinta como si fueran suyos: el mismo texto rojo bajo el campo que saldría de una anotación de datos.

Sin él, un 400 con ValidationProblemDetails acaba en una lista de mensajes encima del formulario, lejos del campo al que acusan y con una maquetación que cada aplicación repite a su manera.

Instalación

dotnet add package Persiltech.Validation.Blazor

El contrato

public sealed class ApiValidator : ComponentBase, IDisposable
{
    public void Show(IReadOnlyDictionary<string, string[]>? errors);
    public void Clear();

    public IReadOnlyList<string> Unmatched { get; }

    [Parameter]
    public EventCallback<IReadOnlyList<string>> UnmatchedChanged { get; set; }
}

Eso es todo. Un componente, dos métodos.

Uso

Se coloca dentro del EditForm, igual que DataAnnotationsValidator, y se le pasan los errores cuando la llamada vuelve:

<EditForm Model="model" OnValidSubmit="SubmitAsync">
    <DataAnnotationsValidator />
    <ApiValidator @ref="validator" />

    <MudTextField For="@(() => model.Email)" @bind-Value="model.Email" Label="Correo" />
    <MudTextField For="@(() => model.Password)" @bind-Value="model.Password" Label="Contraseña" />

    <MudButton ButtonType="ButtonType.Submit">Registrarme</MudButton>
</EditForm>

@code {
    private readonly RegistrationModel model = new();
    private ApiValidator validator = default!;

    private async Task SubmitAsync()
    {
        var result = await Api.RegisterAsync(model);

        validator.Show(result.Errors);
    }
}

El campo tiene que declarar For. Es lo que ata el componente de entrada a una propiedad del modelo; sin eso no hay a qué campo llevar el mensaje. Con las entradas nativas de Blazor (InputText) el papel lo hace @bind-Value.

Las dos validaciones conviven: DataAnnotationsValidator resuelve lo que el navegador puede comprobar solo, y ApiValidator trae lo que solo sabe el servidor. Las dos escriben en el mismo sitio.

Lo que no es de ningún campo

ValidationProblemDetails manda los errores generales con la clave vacía, y una respuesta puede nombrar un campo que este formulario no tiene. Descartarlos los dejaría invisibles, así que se quedan en Unmatched para que los pintes donde decidas:

<ApiValidator @ref="validator" UnmatchedChanged="@(messages => unmatched = messages)" />

@if (unmatched.Count > 0)
{
    <MudAlert Severity="Severity.Error">
        @foreach (var message in unmatched)
        {
            <MudText Typo="Typo.body2">@message</MudText>
        }
    </MudAlert>
}

Cómo encuentra el campo

La clave de la API nombra el campo como lo serializó el servidor, y Blazor lo identifica por el objeto que lo contiene más el nombre de la propiedad. El componente recorre la ruta para dar con ese objeto:

Clave Va a parar a
email model.Email — el emparejamiento ignora mayúsculas
address.city City de model.Address, no del raíz
items[0].name Name del primer elemento, sea lista o arreglo
byCode[abc].name Name del elemento con esa clave, si la colección indexa
"" (vacía) Unmatched
telefono Unmatched, si el modelo no tiene esa propiedad

Una ruta que no existe —una rama sin instanciar, un índice fuera de rango, una clave que no está— no lanza: el mensaje se va a Unmatched. Un formulario no debe caerse por lo que responda el servidor.

Cuándo se retiran los mensajes

  • Al editar el campo, porque el error acusaba al valor que se envió, no al que se está escribiendo.
  • Al volver a enviar, porque lo que dijo la respuesta anterior deja de valer.
  • Al cambiar de modelo. Si el formulario pasa a otro registro —el mismo diálogo que se reabre—, EditForm monta un EditContext nuevo y el componente se muda con él: lo del registro anterior no acompaña al siguiente.
  • Con Clear(), cuando el formulario lo decida.

Con MudBlazor

Funciona sin configurar nada. Está comprobado sobre MudTextField —de una línea y con Lines—, MudSelect, MudAutocomplete, MudNumericField, MudDatePicker, MudRadioGroup, MudCheckBox y MudSwitch: en todos el mensaje sale bajo el control con el mismo aspecto que tendría el de una anotación. Tres cosas que conviene saber:

MudTextField enseña un solo mensaje por campo, el primero. Si tu API devuelve varios para la misma clave, los demás no se ven. Cuando importen todos, júntalos en el servidor en un mensaje o repártelos en claves distintas.

Un control sin For no pinta nada, y su mensaje queda invisible —no en Unmatched, porque la clave sí encontró su propiedad—. Pasa, por ejemplo, con MudSelect en selección múltiple: su For apunta al valor único, no a la lista. La salida es ValidationMessage, que es de Blazor y lee del mismo sitio:

<MudSelect T="string" MultiSelection="true"
           SelectedValues="model.Interests"
           SelectedValuesChanged="OnInterestsChanged"
           Label="Qué te interesa">
    @* … *@
</MudSelect>

<MudText Typo="Typo.caption" Color="Color.Error">
    <ValidationMessage For="@(() => model.Interests)" />
</MudText>

Y ese control tampoco avisa al corregirse, así que el mensaje se quedaría puesto: el formulario lo notifica a mano.

private void OnInterestsChanged(IReadOnlyCollection<string> values)
{
    model.Interests = [.. values];

    editContext.NotifyFieldChanged(new FieldIdentifier(model, nameof(model.Interests)));
}

El paquete no depende de MudBlazor ni de ninguna biblioteca de interfaz: trabaja contra el EditContext, así que sirve igual con las entradas nativas de Blazor.

Decisiones de diseño

  • No habla HTTP. Recibe los errores ya deserializados. Atarlo a un cliente concreto lo haría inservible para quien use otro, y el diccionario por campo es la misma forma en cualquiera de ellos.
  • No pinta nada. Ni un cartel, ni un contenedor: escribe en el EditContext y deja que cada componente de entrada muestre lo suyo. Un componente propio para los errores nunca acabaría pareciéndose del todo al resto del formulario.
  • Lo que no encuentra dueño no se tira. Es la diferencia entre un error que se ve en un sitio imperfecto y un error que no se ve.
  • Nada de dependencias de interfaz. Obligaría a arrastrar MudBlazor a quien no la use.

Compatibilidad

net10.0, sobre Blazor WebAssembly.

Estado

Preliberación: la superficie pública puede cambiar antes de la 1.0.0.

Historial de versiones

El código fuente vive en el monorepo; esta tabla resume qué cambió en cada versión publicada.

Versión Cambios
0.1.0 Primera versión.

Soporte

Para dudas, fallos o peticiones abre una incidencia. También puedes consultar la página del paquete.

Apoyar el desarrollo

Si este paquete te resulta útil, puedes patrocinar su desarrollo.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Persiltech.Validation.Blazor:

Package Downloads
Persiltech.Membership.Blazor

Cliente Blazor de Persiltech.Membership: estado de autenticacion a partir del JWT que emite la API, renovacion automatica de la sesion, el manejador que firma cada peticion y los componentes de MudBlazor de sus pantallas.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 64 9/16/2026

v0.1.0
     - Primera versión: el componente ApiValidator lleva los errores de una API al campo que los provocó
     - Resuelve rutas anidadas e índices —listas, arreglos y tipos con indexador propio— y empareja el camelCase del servidor con la propiedad
     - Lo que no pertenece a ningún campo queda en Unmatched en lugar de perderse
     - El mensaje se retira al editar el campo, al volver a enviar y al cambiar el formulario de modelo
     - Sin dependencias de interfaz: trabaja contra el EditContext, así que sirve con MudBlazor y con las entradas nativas