目录

概述——Blazor ValidationFormState控件

代码和示例

Blazor编辑设置

验证控件

验证器

StringValidator

IValidation

WeatherForecast

ValidationFormState控件

一个简单的实现

总结


概述——Blazor ValidationFormState控件

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

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

代码和示例

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

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

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

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

Blazor编辑设置

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

<EditForm Model="@exampleModel" OnValidSubmit="@HandleValidSubmit"><DataAnnotationsValidator /><ValidationSummary /><InputText id="name" @bind-Value="exampleModel.Name" /><ValidationMessage For="@(() => exampleModel.Name)" /><button type="submit">Submit</button>
</EditForm>

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

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

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

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

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

验证控件

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

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

验证器

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

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

该Validator属性/字段有:

public bool IsValid => !Trip;
public List<string> Messages { get; } = new List<string>();
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 messageif (this.Messages.Count == 0) Messages.Add(message);//set up a FieldIdentifier and //add the message to the Edit Context ValidationMessageStorevar 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. StringValidator是Validator专门为string的实现。

StringValidatorExtensions为string声明了一个静态扩展方法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<string>
{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. ValidationMessageStore是EditContext的ValidationMessageStore。
  6. validating 是一个布尔字段,以确保我们不会堆叠验证。
  7. disposedValue是IDisposable实现的一部分。
[CascadingParameter] public EditContext EditContext { get; set; }
[Parameter] public bool DoValidationOnFieldChange { get; set; } = true;
[Parameter] public EventCallback<bool> 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 EditContextthis.validationMessageStore = new ValidationMessageStore(this.EditContext);// Wires up to the EditContext OnFieldChanged eventif (this.DoValidationOnFieldChange)this.EditContext.OnFieldChanged += FieldChanged;// Wires up to the Editcontext OnValidationRequested eventthis.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方法。我们见过模型。Validate在WesatherForecast数据类中。当它传递一个fieldname到 Validate时,它只清除该特定fieldname的任何验证消息。

private void Validate(string fieldname = null)
{// Checks to see if the Model implements IValidationvar 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 messagesif (string.IsNullOrEmpty(fieldname))this.validationMessageStore.Clear();elsevalidationMessageStore.Clear(new FieldIdentifier(this.EditContext.Model, fieldname));// Run the IValidation interface Validate methodvalidator.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 intothis.EditContext.NotifyValidationStateChanged();// Invoke ValidationStateChangedthis.ValidStateChanged.InvokeAsync(this.IsValid);this.validating = false;}
}

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

public void Clear()=> this.validationMessageStore.Clear();<span class="pl-c">// 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()
{<span class="pl-c">// Do not change this code. // Put cleanup code in 'Dispose(bool disposing)' methodDispose(disposing: true);GC.SuppressFinalize(this);
}

一个简单的实现

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

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

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

@using Blazor.Database.Data
@page "/validationeditor"<EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit"><EditFormState @ref="editFormState" EditStateChanged="this.EditStateChanged"></EditFormState><ValidationFormState @ref="validationFormState"></ValidationFormState><label class="form-label">ID:</label> <InputNumber class="form-control" @bind-Value="Model.ID" /><label class="form-label">Date:</label> <InputDate class="form-control" @bind-Value="Model.Date" /><ValidationMessage For="@(() => Model.Date)" /><label class="form-label">Temp C:</label> <InputNumber class="form-control" @bind-Value="Model.TemperatureC" /><ValidationMessage For="@(() => Model.TemperatureC)" /><label class="form-label">Summary:</label> <InputText class="form-control" @bind-Value="Model.Summary" /><ValidationMessage For="@(() => Model.Summary)" /><div class="mt-2"><div>Validation Messages:</div><ValidationSummary /></div><div class="text-right mt-2"><button class="btn @btnStateColour" disabled>@btnStateText</button><button class="btn @btnValidColour" disabled>@btnValidText</button><button class="btn btn-primary" type="submit" disabled="@_btnSubmitDisabled">Submit</button></div></EditForm>
@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. 从ValidationMessage的For属性生成的FieldIdentifier与验证存储中的FieldIdentifier不匹配。检查您正在生成并登录到验证存储的FieldIdentifier。

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

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

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

Blazor验证控件相关推荐

  1. webform里的验证控件

    1.非空验证控件:RequireFieldValidator  :2.数据比较验证:CompareValidator :3.数据范围验证:RangeValidator :4.正则表达式验证:Regul ...

  2. NET中验证控件表达式汇总

    ASP.NET为开发人员提供了一整套完整的服务器控件来验证用户输入的信息是否有效.这些控件如下: 1.RequiredFieldValidator:验证一个必填字段,如果这个字段没填,那么,将不能提交 ...

  3. Asp.Net中验证控件简单分析

    为了实现验证,.net提供了一个javascript脚本,当我们生成一个asp.net页面时,通过查看页面源代码的方式,会发现都调用了下面一个javascript文件: <script src= ...

  4. WebForm服务器验证控件与前端js自定义验证共同使用

        问题: 前端aspx页面中需要在button中添加OnClientClick事件后,这个OnClientClick所执行的自定义的客户端js验证:这个时候,所有的服务器验证控件都会失效!   ...

  5. [jQuery]使用jQuery.Validate进行客户端验证(高级篇-下)——不使用微软验证控件的理由...

    2019独角兽企业重金招聘Python工程师标准>>> 引用源:http://www.cnblogs.com/kyo-yo/archive/2010/07/06/Use-jQuery ...

  6. 关于页面有多个验证控件和多个按钮的问题

    今天需要将petshop进行最后的完善发现 发现当页面有多个按钮时,验证控件仍然被随时出发,换句话说就是 只要有按钮出现时   验证控件就要被出发 从网上寻找答案  很快又了结果: 验证控件和按钮都有 ...

  7. ASP.NET 验证控件

    ASP.NET公有六种验证控件,分别如下: 控件名 功能描叙 RequiredFieldValidator(必须字段验证) 用于检查是否有输入值 CompareValidator(比较验证) 按设定比 ...

  8. 风影ASP.NET基础教学 5 验证控件(二) 扩展自己的验证控件

    前文回顾 我们之前提过了5种官方验证控件的方式. 下面我们来讲解编程方式验证和扩展自己的验证控件 以编程的方式验证asp.net控件 默认情况下,在页面回发到服务器时,页面初始化之后(即视图ViewS ...

  9. 自定义用户验证控件CustomValidator

    背景:VisualStudio2005; 使用自定义控件判断注册用户名是否已经存在: 实现: html: <%@ Page Language="C#" AutoEventWi ...

最新文章

  1. ios开发趋势_2020年将成为iOS应用开发的主要趋势
  2. 信息安全系统设计基础期末总结
  3. arm linux 显示屏 10钟黑屏
  4. python之获取目录下的文件名
  5. 三大新兴力量,引领中国消费新经济丨图媒体
  6. linux如何自动调jiaob,最牛B的 Linux Shell 命令 系列连载(四)
  7. aes子密钥生成c语言_一种基于流密码算法的子密钥生成方法与流程
  8. php 数据类型 map,es6中Set和Map的对比介绍(附代码)
  9. cocos2d-x关于CCTableView的“乱序问题”的理解
  10. 老司机带你用python爬取妹子图,接稳这波福利
  11. 大数据专业认知实习作业
  12. 通过jenkins+gitlab构建CI/CD流水线
  13. 异常点检测算法分析与选择
  14. UVa 106 Fermat vs. Pythagoras(毕达哥拉斯定理)
  15. android 系统时间同步
  16. 开源SDK实现Android视频直播
  17. 蓝桥杯训练1:质数判断,同余问题
  18. 三论 (信息论、控制论、系统论的合称)
  19. sqlserver 登录方式修改,由默认的windows账户改为用sa等sql server账户登录问题
  20. grafana绘图配置查询变量+多级变量联动

热门文章

  1. php获取td数据,记一次用PHP做爬虫获取全市高考报考数据
  2. 计算机组成原理944考试,郑州大学2019年硕士研究生入学考试 《944计算机组成原理》考试大纲...
  3. hibernate mysql 注解_【译】Spring 4 + Hibernate 4 + Mysql + Maven集成例子(注解 + XML)
  4. 计算机加经济学加自动化,MIT经济学家戳破机器人真相:除了能取代你,价值微乎其微...
  5. char添加一个字符_LINUX字符设备驱动模型分析(起始篇)
  6. Adobe Illustrator的教程等距购物移动应用程序
  7. python输入4个数字_输入4个整数,要求按从小到大的顺序输出python
  8. C++ 运算符重载的原理
  9. Mac中安装NetBeans方法
  10. 近期code(11.16)