Come sappiamo, Blazor supporta nativamente le data annotation per la validazione dei dati in input: grazie a questa funzionalità, possiamo per esempio decorare una classe User con attributi che ne specifichino i requisiti formali:
public class User { [Required] public string Username { get; set; } [RegularExpression(Constants.UK_PHONE_NUMBER)] public string MobilePhone { get; set; } [RegularExpression(Constants.UK_PHONE_NUMBER)] public string LandLinePhone { get; set; } }
A questo punto, è sufficiente aggiungere un DataAnnotationValidator (e magari anche un ValidationSummary) al componente EditForm, e il motore di rendering si occuperà automaticamente di verificarne la correttezza prima di procedere al submit:
<EditForm Model="this.NewUser" OnValidSubmit="this.SubmitAsync" class="col-6"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="form-group"> <label>Username</label> <InputText class="form-control" @bind-Value="this.NewUser.Username" /> </div> <div class="form-group"> <label>Mobile Phone</label> <InputText class="form-control" @bind-Value="this.NewUser.MobilePhone" /> </div> <div class="form-group"> <label>Landline phone</label> <InputText class="form-control" @bind-Value="this.NewUser.LandLinePhone" /> </div> <button type="submit" class="btn btn-primary">Save</button> </EditForm> @Code { public User NewUser { get; set; } public async Task SubmitAsync() { // .. logica per memorizzare l'utente qui ... } }
Nel nostro caso, se provassimo a salvare uno user con dati errati, gli errori ci verrebbero evidenziati come in figura.
Purtroppo le data annotation funzionano bene con regole semplici, ma non sono idonee a rappresentare logiche di validazione più complesse, per esempio quelle che coinvolgono diverse proprietà.
Per questi scopi, una delle possibili soluzioni in Blazor è quella di costruire un custom validator. Immaginiamo di voler far sì che almeno uno tra Mobile Phone e Landline Phone sia obbligatorio. Possiamo implementare questa logica in una classe UserPhoneValidator simile alla seguente:
public class UserPhoneValidator : ComponentBase { private ValidationMessageStore _store; [CascadingParameter] public EditContext Context { get; set; } protected override void OnInitialized() { base.OnInitialized(); _store = new ValidationMessageStore(this.Context); this.Context.OnValidationRequested += Context_OnValidationRequested; ; this.Context.OnFieldChanged += Context_OnFieldChanged; } private void Context_OnValidationRequested(object sender, ValidationRequestedEventArgs e) { this.ExecuteValidation(); } private void Context_OnFieldChanged(object sender, FieldChangedEventArgs e) { if (e.FieldIdentifier.FieldName == "MobilePhone" || e.FieldIdentifier.FieldName == "LandLinePhone") { this.ExecuteValidation(); } } // .. altro codice qui .. }
La nostra classe eredita da ComponentBase, così che possiamo utilizzarla nel markup di Blazor, ed espone una proprietà di tipo EditContext. Si tratta di un CascadingParameter che viene automaticamente assegnato dalla form all'interno della quale poniamo il nostro componente, così che possiamo sia accederne al contenuto, sia ricevere una notifica al verificarsi di alcuni eventi.
Nel nostro caso, abbiamo sottoscritto gli eventi OnValidationRequested e OnFieldChanged, per eseguire la nostra logica sia al submit della form stessa, sia quando il valore di un field viene modificato. In quest'ultimo caso, come possiamo notare, ci preoccupiamo solo dei field relativi ai due numeri di telefono che vogliamo monitorare.
Il codice di ExecuteValidation è abbastanza semplice e sfrutta un ValidationMessageStore per inviare gli esiti della validazione alla form:
private void ExecuteValidation() { var model = (this.Context.Model as User); if (model == null) return; // this only works with User objects _store.Clear(); if (string.IsNullOrWhiteSpace(model.MobilePhone) && string.IsNullOrWhiteSpace(model.LandLinePhone)) { _store.Add(() => model.MobilePhone, "Either mobile or landline phone must be provided"); _store.Add(() => model.LandLinePhone, "Either mobile or landline phone must be provided"); } }
Come primo passo, puliamo lo store da eventuali messaggi precedenti. Poi, se entrambi i numeri di telefono sono vuoti, aggiungiamo una entry per ciascuno di queste due proprietà.
L'ultimo aspetto da non dimenticare è quello di implementare IDisposable, come in tutti i casi in cui sottoscriviamo eventi, per evitare memory leak:
public class UserPhoneValidator : ComponentBase, IDisposable { // .. altro codice qui .. public void Dispose() { this.Context.OnValidationRequested -= Context_OnValidationRequested; ; this.Context.OnFieldChanged -= Context_OnFieldChanged; } }
A questo punto, possiamo finalmente aggiungere il nostro validatore alla form in pagina:
<EditForm Model="this.NewUser" OnValidSubmit="this.Submit" class="col-6"> <DataAnnotationsValidator /> <UserPhoneValidator /> <ValidationSummary /> ... <button type="submit" class="btn btn-primary">Save</button> </EditForm>
Se abbiamo svolto tutti i passaggi correttamente, vedremo il messaggio di errore apparire contemporaneamente su entrambe le proprietà nel caso siano tutte e due vuote, e sparire nel momento in cui ne valorizziamo una.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
C# 12: Cosa c'è di nuovo e interessante
Limitare le richieste lato server con l'interactive routing di Blazor 8
Creare gruppi di client per Event Grid MQTT
Potenziare Azure AI Search con la ricerca vettoriale
Aggiungere interattività lato server in Blazor 8
Copiare automaticamente le secret tra più repository di GitHub
Usare ASP.NET Core dev tunnels per testare le applicazioni su internet
Modificare i metadati nell'head dell'HTML di una Blazor Web App
Eseguire query manipolando le liste contenute in un oggetto mappato verso una colonna JSON
Eseguire le GitHub Actions offline
Utilizzare ChatGPT con Azure OpenAI
Miglioramenti agli screen reader e al contrasto in Angular