Blazor验证控件

Blazor验证控件目录概述 BlazorValida 控件代码和示例 Blazor 编辑设置验证控件验证器 StringValida 控件一个简单的实现总结概述 BlazorValida 控件这是描述一组有用的 BlazorEdit 控件的系列文章中的第二篇 这些控件解决了开箱即用编辑体验中的一些当前缺点 而无需购买昂贵的工具包

目录

概述——Blazor ValidationFormState控件

代码和示例

Blazor编辑设置

验证控件

验证器

StringValidator

IValidation

WeatherForecast

ValidationFormState控件

一个简单的实现

总结


概述——Blazor ValidationFormState控件

这是描述一组有用的Blazor Edit控件的系列文章中的第二篇,这些控件解决了开箱即用编辑体验中的一些当前缺点,而无需购买昂贵的工具包。

本文介绍了表单验证的工作原理,并展示了如何从头开始构建一个相对简单但功能齐全的验证系统。一旦定义了基本结构和类,就很容易为自定义类的任何新验证要求或验证器编写额外的验证链方法。

Blazor验证控件

代码和示例

该存储库包含一个项目,该项目实现了本系列中所有文章的控件。你可以在这里找到它。

示例站点位于https://cec-blazor-database.azurewebsites.net/

本文末尾描述的示例表单可以在https://cec-blazor-database.azurewebsites.net//validationeditor 中看到。

Repo是未来文章的一项正在进行的工作,因此会发生变化和发展。

Blazor编辑设置

首先,让我们看看开箱即用的表单控件以及验证的工作原理。一个经典的形式看起来像这样:

 
    
     
     
     
     
     
   

第一篇文章介绍的基本相互作用EditFormEditContext因此我们将跳过该部分并将精力集中在验证过程。

当用户单击提交按钮时,EditForm可以:

  1. 如果使用OnSubmit注册了委托,则会触发它并忽略验证。
  2. 如果没有OnSubmit委托,它会调用EditContext.Validate.。根据结果​​触发OnValidSubmitOnInvalidSubmit

EditContext.Validate检查是否有为OnValidationRequested注册的委托,如果有,则同步运行它。完成后,它会检查ValidationMessageStore中是否有任何消息。如果为空,则表单通过验证并被OnValidSubmit调用,否则OnInvalidSubmit被调用。

Validator是一个没有发出标记的表单组件。它被放置在EditForm中并捕获级联的EditContext.。在初始化时,它注册一个事件处理程序EditContext.OnValidationRequested以触​​发验证。在验证时,验证器执行它编码要做的任何事情,将验证失败消息记录到EditContext ValidationMessageStore并最终调用EditContext.NotifyValidationStateChanged,其将触发 EditContext.OnValidationStateChanged

验证控件

诸如ValidationMessageValidationSummary捕获级联EditContex和在EditContext.OnValidationStateChanged上注册事件处理程序之类的控件。触发时,它们会检查任何相关消息并显示它们。

在上面显示的窗体中,
DataAnnotationsValidator控件添加到窗体中。这如上所述挂钩,并使用模型类上的自定义属性注释来验证值。

验证器

Validator是基本的验证器类。它被声明额为abstract并使用泛型。验证器按照链接原则工作。基类包含所有常见的样板代码。

  1. 第一次调用是针对要验证的对象类型定义的扩展方法。每个对象类型都需要自己的扩展方法来调用其特定的验证器。此扩展方法返回对象类型的适当验证器。
  2. 拥有验证器实例后,您可以将任意数量的验证方法链接在一起。每个都经过编码以运行其验证测试,将任何特定消息记录到验证器,必要时触发行程,并返回验证器实例。
  3. 验证通过调用Validate完成,如有必要,它会触发传递的绊线,并将所有验证消息记录到ValidationMessageStore中。

Validator属性/字段有:

public bool IsValid => !Trip; public List 
   
     Messages { get; } = new List 
    
      (); protected bool Trip { get; set; } = false; protected string FieldName { get; set; } protected T Value { get; set; } protected string DefaultMessage { get; set; } = "The value failed validation"; protected ValidationMessageStore ValidationMessageStore { get; set; } protected object Model { get; set; } 
     
   

构造函数填充validator

public Validator(T value, string fieldName, object model, ValidationMessageStore validationMessageStore, string message) { this.FieldName = fieldName; this.Value = value; this.Model = model; this.ValidationMessageStore = validationMessageStore; this.DefaultMessage = string.IsNullOrWhiteSpace(message) ? this.DefaultMessage : message; }

有两种Validate方法:一种供外部使用的public方法,另一种供特定验证器覆盖的protected方法。

public virtual bool Validate(ref bool tripwire, string fieldname, string message = null) { if (string.IsNullOrEmpty(fieldname) || this.FieldName.Equals(fieldname)) { this.Validate(message); if (!this.IsValid) tripwire = true; } else this.Trip = false; return this.IsValid; }
protected virtual bool Validate(string message = null) { if (!this.IsValid) { message ??= this.DefaultMessage; // Check if we've logged specific messages. If not, add the default message if (this.Messages.Count == 0) Messages.Add(message); //set up a FieldIdentifier and //add the message to the Edit Context ValidationMessageStore var fi = new FieldIdentifier(this.Model, this.FieldName); this.ValidationMessageStore.Add(fi, this.Messages); } return this.IsValid; } protected void LogMessage(string message) { if (!string.IsNullOrWhiteSpace(message)) Messages.Add(message); }

StringValidator

让我们看一下StringValidator验证器的示例实现。全套验证器在Repo中。有两个类:

  1. StringValidatorExtensions是一个声明为string扩展方法的static类。
  2. StringValidatorValidator专门为string的实现。

StringValidatorExtensionsstring声明了一个静态扩展方法Validation。它返回一个StringValidator实例。在任何string上调用StringValidator来初始化验证链。

public static class StringValidatorExtensions { public static StringValidator Validation(this string value, string fieldName, object model, ValidationMessageStore validationMessageStore, string message = null) { var validation = new StringValidator(value, fieldName, model, validationMessageStore, message); return validation; } }

StringValidator继承自Validator并声明了strings的特定验证链方法。每个都运行它的测试。如果验证失败,它会将任何提供的消息记录到消息存储中并触发绊线。最后,它返回this。对于string,我们有两种长度方法和一种覆盖大多数情况的RegEx方法。

public class StringValidator : Validator 
   
     { public StringValidator(string value, string fieldName, object model, ValidationMessageStore validationMessageStore, string message) : base(value, fieldName, model, validationMessageStore, message) { } public StringValidator LongerThan(int test, string message = null) { if (string.IsNullOrEmpty(this.Value) || !(this.Value.Length > test)) { Trip = true; LogMessage(message); } return this; } public StringValidator ShorterThan(int test, string message = null) { if (string.IsNullOrEmpty(this.Value) || !(this.Value.Length < test)) { Trip = true; LogMessage(message); } return this; } public StringValidator Matches(string pattern, string message = null) { if (!string.IsNullOrWhiteSpace(this.Value)) { var match = Regex.Match(this.Value, pattern); if (match.Success && match.Value.Equals(this.Value)) return this; } this.Trip = true; LogMessage(message); return this; } } 
   

IValidation

IValidation接口看起来是这样的。它只是定义了一个Validate方法。

public interface IValidation { public bool Validate(ValidationMessageStore validationMessageStore, string fieldname, object model = null); }

WeatherForecast

WeatherForecast 是典型的数据类。

  1. 它实现IValidation以便控件可以运行验证。
  2. 每个字段都声明为具有默认值的属性。
  3. 它实现了调用三个验证的IValidation.Validate

每次验证:

  1. 调用Validation类型的扩展方法。
  2. 调用一个或多个验证链方法。
  3. 调用Validate以将任何验证消息记录到EditContext上的ValidationMessageStore中,并且在必要时触发绊线。

public class WeatherForecast : IValidation { public int ID { get; set; } = -1; public DateTime Date { get; set; } = DateTime.Now; public int TemperatureC { get; set; } = 0; [NotMapped] public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } = string.Empty; public bool Validate(ValidationMessageStore validationMessageStore, string fieldname, object model = null) { model = model ?? this; bool trip = false; this.Summary.Validation("Summary", model, validationMessageStore) .LongerThan(2, "Your description needs to be a little longer! 3 letters minimum") .Validate(ref trip, fieldname); this.Date.Validation("Date", model, validationMessageStore) .NotDefault("You must select a date") .LessThan(DateTime.Now.AddMonths(1), true, "Date can only be up to 1 month ahead") .Validate(ref trip, fieldname); this.TemperatureC.Validation("TemperatureC", model, validationMessageStore) .LessThan(70, "The temperature must be less than 70C") .GreaterThan(-60, "The temperature must be greater than -60C") .Validate(ref trip, fieldname); return !trip; } }

ValidationFormState控件

ValidationFormState控件替换了Blazor提供的基本Validator控件。

  1. 它捕获级联的EditContext.
  2. DoValidationOnFieldChange控制字段级验证。如果是true,它会在用户退出字段时验证该字段。如果是false,它仅通过EditContext响应表单级验证请求。
  3. ValidStateChanged 是父级在需要时附加事件处理程序的回调。
  4. IsValid是一个公开当前验证状态的public readonly属性。它检查EditContext是否有任何验证消息。
  5. ValidationMessageStoreEditContextValidationMessageStore
  6. validating 是一个布尔字段,以确保我们不会堆叠验证。
  7. disposedValueIDisposable实现的一部分。

[CascadingParameter] public EditContext EditContext { get; set; } [Parameter] public bool DoValidationOnFieldChange { get; set; } = true; [Parameter] public EventCallback 
   
     ValidStateChanged { get; set; } public bool IsValid => !EditContext?.GetValidationMessages().Any() ?? true; private ValidationMessageStore validationMessageStore; private bool validating = false; private bool disposedValue; 
   

当组件初始化时,它从EditContext中获取ValidationMessageStore 。它检查它的是否运行字段级验证,如果是的话,使用EditContext.OnFieldChanged事件注册FieldChanged。最后,它使用EditContext.OnValidationRequested注册ValidationRequested

protected override Task OnInitializedAsync() { Debug.Assert(this.EditContext != null); if (this.EditContext != null) { // Get the Validation Message Store from the EditContext this.validationMessageStore = new ValidationMessageStore(this.EditContext); // Wires up to the EditContext OnFieldChanged event if (this.DoValidationOnFieldChange) this.EditContext.OnFieldChanged += FieldChanged; // Wires up to the Editcontext OnValidationRequested event this.EditContext.OnValidationRequested += ValidationRequested; } return Task.CompletedTask; }

两个事件处理程序调用Validate,一个有字段名,一个没有字段名。

private void FieldChanged(object sender, FieldChangedEventArgs e) => this.Validate(e.FieldIdentifier.FieldName); private void ValidationRequested(object sender, ValidationRequestedEventArgs e) => this.Validate();

Validate里面的评论解释了它在做什么。它将 Model转换为IValidator并检查它是否有效。如果是,则调用接口上的Validate方法。我们见过模型。ValidateWesatherForecast数据类中。当它传递一个fieldname Validate时,它只清除该特定fieldname的任何验证消息。

private void Validate(string fieldname = null) { // Checks to see if the Model implements IValidation var validator = this.EditContext.Model as IValidation; if (validator != null || !this.validating) { this.validating = true; // Check if we are doing a field level or form level validation // Form level - clear all validation messages // Field level - clear any field specific validation messages if (string.IsNullOrEmpty(fieldname)) this.validationMessageStore.Clear(); else validationMessageStore.Clear (new FieldIdentifier(this.EditContext.Model, fieldname)); // Run the IValidation interface Validate method validator.Validate(validationMessageStore, fieldname, this.EditContext.Model); // Notify the EditContext that the Validation State has changed // This precipitates a OnValidationStateChanged event // which the validation message controls are all plugged into this.EditContext.NotifyValidationStateChanged(); // Invoke ValidationStateChanged this.ValidStateChanged.InvokeAsync(this.IsValid); this.validating = false; } }

其余代码由实用程序方法和IDisposable实现组成。

public void Clear() => this.validationMessageStore.Clear(); // IDisposable Implementation protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { if (this.EditContext != null) { this.EditContext.OnFieldChanged -= this.FieldChanged; this.EditContext.OnValidationRequested -= this.ValidationRequested; } } disposedValue = true; } } public void Dispose() { // Do not change this code. // Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); }

一个简单的实现

为了测试组件,这里有一个简单的测试页面。

上下更改温度,您应该会看到按钮更改颜色和文本,以及启用/禁用状态。将温度更改为 200以获取验证消息。

您可以在https://cec-blazor-database.azurewebsites.net//validationeditor 上看到这一点。

@using Blazor.Database.Data @page "/validationeditor" 
    
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    
Validation Messages:
@code { protected bool _isDirty = false; protected bool _isValid => validationFormState?.IsValid ?? true; protected string btnStateColour => _isDirty ? "btn-danger" : "btn-success"; protected string btnStateText => _isDirty ? "Dirty" : "Clean"; protected string btnValidColour => !_isValid ? "btn-danger" : "btn-success"; protected string btnValidText => !_isValid ? "Invalid" : "Valid"; protected bool _btnSubmitDisabled => !(_isValid && _isDirty); protected EditFormState editFormState { get; set; } protected ValidationFormState validationFormState { get; set; } private WeatherForecast Model = new WeatherForecast() { ID = 1, Date = DateTime.Now, TemperatureC = 22, Summary = "Balmy" }; private void HandleValidSubmit() => this.editFormState.UpdateState(); private void EditStateChanged(bool editstate) => this._isDirty = editstate; }

总结

希望我已经解释了验证的工作原理以及如何构建一个简单但全面且可扩展的验证系统。

验证最常见的问题是ValidationMessage控件不显示消息。造成这种情况的原因通常有两个:

  1. UI没有更新。单步执行代码以检查何时发生了什么。
  2. ValidationMessageFor属性生成的FieldIdentifier与验证存储中的FieldIdentifier不匹配。检查您正在生成并登录到验证存储的FieldIdentifier

下一篇文章将展示如何在表单变脏时锁定表单并阻止导航。

如果您在以后发现这篇文章很好,最新版本将在此处提供

https://www.codeproject.com/Articles//A-Blazor-Validation-Control

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203867.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午9:25
下一篇 2026年3月19日 下午9:25


相关推荐

  • 《深入理解mybatis原理》 MyBatis缓存机制的设计与实现

    《深入理解mybatis原理》 MyBatis缓存机制的设计与实现本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。

    2022年5月11日
    48
  • MQTT服务器搭建–Mosquitto[通俗易懂]

    MQTT服务器搭建–Mosquitto[通俗易懂]MQTT服务器搭建–Mosquitto1.Mosquitto简介MQTT(MQTelemetryTransport),消息队列遥测传输协议,轻量级的发布/订阅协议, 适用于一些条件比较苛刻的环境,进行低带宽、不可靠或间歇性的通信。目前已经是物联网消息通信事实上的标准协议了。值得一提的是mqtt提供三种不同质量的消息服务:l “至多一次”:消息发布完全依赖底层 TCP/IP 网络。

    2022年5月6日
    42
  • 金融公司2014年度工作总结与2015年度工作计划

    金融公司2014年度工作总结与2015年度工作计划正文:一、 员工个人对全年重点工作、核心绩效指标进行罗列。自6月入职以来参加730、825、917、1015、1126的版本测试,测试的各个功能点,前台涉及的测试功能点包括广发、汇添富的申购、赎回,基金双持,票据购买、冻结收银台、理财管理、后台功能点包括用户开户信息查询等各种报表处理、申购异常处理、赎回异常处理、强制赎回等异常处理、定时任务管理、日常管理包括基础配置管理、基金管理中的…

    2022年7月26日
    9
  • android广播注册方式_安卓广播接收器

    android广播注册方式_安卓广播接收器前面分析了Android系统的广播机制,从本质来说,它是一种消息订阅/发布机制。因此,使用这种消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息其实就是注册广播接收器。       接下来,我们继续分析Android应用程序是如何注册广播接收器的,以及把广播接收器注册到哪里去的。       在Android的广播机制中,ActivityManagerServi

    2025年10月26日
    3
  • 我用AI干掉了自己的工作,然后发现了新机会

    我用AI干掉了自己的工作,然后发现了新机会

    2026年3月15日
    1
  • 驱动开发利器Microsoft Windows Driver Kit 7.1.0下载

    在Windows2000与WindowsXP系统采用是WINDDK来开发WINDOWS驱动程序,我手头也有WINDDK,可是从WindowsVista开始之后,一般采用MicrosoftW

    2021年12月26日
    64

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号