- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-blazor
基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。
本篇围绕 Ant Design Blazor(ant-design-blazor)中官方示例《设置验证信息》(set-validate-message.md)展开,讲解一个核心场景:表单验证并不只能依赖模型特性或前端规则,你可以在任何时刻通过表单实例方法SetValidationMessages为指定字段写入(或清空)验证信息——最典型的用途就是把服务端异步校验的错误(如"用户名已被占用")回填到对应表单项上。读完本篇,你将掌握该方法的签名与使用方式、它与EditContext/FieldIdentifier/FormItem的底层调用链,以及如何在测试中验证其行为。
场景:验证信息可以"随时"被设置
官方示例的一句话说明是:"可在任何时候设置验证信息。" 这句话点出了 Ant Design Blazor Form 与原生 BlazorEditForm的一个重要差异:验证提示并非只由DataAnnotations特性或Rules单向决定,表单允许你在提交回调、异步操作完成、甚至任意事件处理后,主动向某个字段注入错误信息。
典型业务流如下:
- 用户填写表单(如注册页),前端验证(
Required等特性)通过后触发OnFinish; - 应用发起服务端校验(
Task.Delay(1000)模拟网络往返),发现"用户名"已被占用; - 拿到服务端错误后,调用
_form.SetValidationMessages("Username", ["Username has error."]),把错误直接挂到Username字段上,输入框下方立刻出现红色错误提示。
这正是官方演示 SetValidateMessage.razor 所做的事情。
官方示例代码逐段解析
完整示例如下(来自 SetValidateMessage.razor):
@using System.ComponentModel.DataAnnotations @using System.Text.Json @using System.ComponentModel <Form Model="@_model" OnFinish="OnFinish" LabelColSpan="8" WrapperColSpan="16" @ref="_form"> <FormItem Label="User Name"> <Input @bind-Value="@context.Username" /> </FormItem> <FormItem WrapperColOffset="8" WrapperColSpan="16"> <Button Type="ButtonType.Primary" HtmlType="submit"> Submit </Button> <Button Type="ButtonType.Primary" HtmlType="button"> Clear </Button> </FormItem> </Form> @code { public class Model { [Required] public string Username { get; set; } } private Model _model = new Model(); Form<Model> _form; private async Task OnFinish(EditContext editContext) { await Task.Delay(1000); // occurs error from server // 服务端返回错误后,把错误信息设置到 Username 字段 _form?.SetValidationMessages("Username", ["Username has error."]); } }关键点说明:
@ref="_form"拿到表单实例:Form<Model>实现了IForm接口,通过@ref持有引用后才能调用实例方法SetValidationMessages、Submit()、Validate()、Reset()等。field参数是字段名,不是FieldIdentifier:传"Username"即可,内部会自动解析成EditContext.Field("Username")得到FieldIdentifier,再按它匹配到对应的FormItem。errorMessages是字符串数组:一个字段可以同时挂多条错误信息;传入空数组则等价于把该字段标记为有效(清空错误),见下文源码解析。OnFinish的时机:只有当所有字段前端验证通过时,OnFinish才会被触发(否则触发OnFinishFailed),因此示例中SetValidationMessages出现在OnFinish内部,表示"本地规则都过了,但服务端拒绝了"。- 模型上的
[Required]:Username标注了Required特性,空值会在提交时先被前端拦截,走不到OnFinish。
源码实现:从 Form 到 FormItem 的调用链
Form.SetValidationMessages:按 FieldIdentifier 定位表单项
方法实现位于 Form.razor.cs:
/// <summary> /// Set validation messages to a specific field. /// </summary> /// <param name="field">The field name</param> /// <param name="errorMessages">The error messages</param> public void SetValidationMessages(string field, string[] errorMessages) { var fieldIdentifier = _editContext.Field(field); var formItem = _formItems .FirstOrDefault(t => t.GetFieldIdentifier().Equals(fieldIdentifier)); formItem?.SetValidationMessage(errorMessages); }从源码结构看,整个流程分三步:
_editContext.Field(field):利用表单内部的EditContext把字段名转换为强类型的FieldIdentifier(包含模型类型与属性名);- 在
_formItems集合(所有注册到表单的FormItem)中,用GetFieldIdentifier()找到与目标字段绑定的那一项; - 通过
IFormItem.SetValidationMessage把错误信息写入该表单项。若找不到对应FormItem(例如该字段没有对应的表单项),由于使用了?.,调用会被安全忽略。
FormItem.SetValidationMessage:驱动 UI 状态与重渲染
FormItem侧的实现位于 FormItem.razor.cs:
void IFormItem.SetValidationMessage(string[] errorMessages) { _validationMessages = errorMessages; _isValid = errorMessages.Length == 0; _validateStatus = _isValid ? FormValidateStatus.Default : FormValidateStatus.Error; _onValidated(_validationMessages); _vaildateStatusChanged?.Invoke(); InvokeAsync(StateHasChanged); }这段代码解释了该方法的几个行为边界:
- 错误与有效状态联动:消息数组为空 → 字段视为有效(
FormValidateStatus.Default);非空 →FormValidateStatus.Error,此时输入框会呈现错误边框、字段下方出现.ant-form-item-explain-error错误文案; - 手动设置会覆盖规则验证结果:它直接覆写
_validationMessages,也就是说你在OnFinish中设置的服务器错误,会取代该字段上一轮规则验证产生的提示; - 会触发验证回调与重渲染:
_onValidated、_vaildateStatusChanged事件及StateHasChanged保证 UI 即时刷新,因此该方法可以"在任何时候"调用——这正是示例标题所强调的语义。
接口契约:IForm
SetValidationMessages是 IForm 接口 的公开成员之一,接口注释中给出的最小用法是:
<Form @ref="form"> <FormItem> <Input @bind-value="model.Name" /> </FormItem> </Form> @code { private IForm _form; private void SetError() { _form.SetValidationMessages("name", new[] { "error message" }); } }Form 公开 API 一览(来自 Form 文档 的"引用实例"一节):
| 成员 | 说明 |
|---|---|
EditContext | 获取 Form 当前的 EditContext |
IsModified | 表单值是否被修改过 |
Model | Form 绑定的数据对象 |
Name | 表单名称 |
Reset() | 重置表单值和验证信息 |
SetValidationMessages(string field, string[] errorMessages) | 给指定的字段设置验证信息 |
Submit() | 验证通过触发 OnFinish,否则触发 OnFinishFailed |
Validate() | 验证所有字段 |
从这张表可以推断出完整的"错误生命周期":规则验证产生错误 →SetValidationMessages随时改写错误 →Reset()兜底清空。
测试用例:如何验证"设置即生效"
仓库中的测试 Form.UpdateValidationMessageTest.razor 对这一能力做了端到端断言:
[Fact] public void Form_UpdateValidateMessage() { //Arrange var cut = Render( @<Form Model="@_model" @ref="form"> <FormItem @ref="formItem"> <AntDesign.Input @bind-Value=@_model.ValidateField /></FormItem> <FormItem > <Button Type="@ButtonType.Primary" HtmlType="submit">Submit</Button> </FormItem> </Form> ); form.SetValidationMessages(nameof(_model.ValidateField), ["Error message"]); cut.Find(".ant-form-item-explain-error").Text().Trim().Should().Be("Error message"); form.SetValidationMessages(nameof(_model.ValidateField), ["New Error message"]); cut.Find(".ant-form-item-explain-error").Text().Trim().Should().Be("New Error message"); }该测试验证了两点:
- 渲染路径:手动设置的错误会渲染到
.ant-form-item-explain-error元素中,与规则验证产生的错误走同一套 UI; - 幂等更新:第二次调用会覆盖第一次的文案,而不是追加。
测试中还用nameof(_model.ValidateField)传字段名,比硬编码字符串更不易出错,推荐在实际项目中使用同样写法。
使用边界与注意事项
结合上述源码与测试,实际使用SetValidationMessages时需注意:
- 字段名必须能对应到表单中的
FormItem:内部通过FieldIdentifier精确匹配,字段不存在或没有绑定输入控件的表单项时,调用不产生任何效果(静默失败),因此建议用nameof保证名称正确; - 传入空数组可清除错误:源码中
_isValid = errorMessages.Length == 0,所以"清除某字段的服务端错误"只需传[]; - 手动设置优先于规则验证:它直接覆写字段的验证消息,因此若该字段还有
Rules,下一次由规则触发的验证会重新生成消息,手动设置的内容可能被规则结果替换; - 它与其他定制错误信息的机制互补:静态文案定制(
DataAnnotations的ErrorMessage、FormValidationRule.Message、Locale模板、ConfigProvider全局模板)解决"错误长什么样",而SetValidationMessages解决"错误从哪来"——特别是来自服务端的动态错误; - 适用前提:方法要求表单已初始化出
EditContext,即<Form Model="@_model">已正常渲染完成后再调用;在OnInitialized等早于渲染的阶段调用会拿不到FormItem。
小结
Ant Design Blazor 的Form.SetValidationMessages(string field, string[] errorMessages)是连接"服务端验证"与"前端表单 UI"的桥梁:它基于EditContext.Field解析字段、按FieldIdentifier定位FormItem,再通过IFormItem.SetValidationMessage覆写验证消息并触发重渲染,支持在OnFinish等任意时机注入、更新乃至清空字段级错误。配合 SetValidateMessage.razor 示例与 测试用例,即可覆盖"提交 → 服务端报错 → 回填字段提示"这一最常见的表单错误处理链路。
- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-blazor
基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。
相关推荐
告别表单校验烦恼:Ant Design跨字段验证实战指南
告别表单校验烦恼:Ant Design跨字段验证实战指南 你是否还在为表单中多字段关联校验头疼?比如密码二次确认必须一致、开始日期不能晚于结束日期、某些字段只有
UI组件前端设计系统Ant Design Blazor 抽屉实战:在列表页实现用户信息预览 Drawer
Ant Design Blazor 抽屉实战:在列表页实现用户信息预览 Drawer Drawer(抽屉)是 Ant Design Blazor 中用于「当前页
UI组件前端Ant Design表单联动验证:字段依赖与关联校验
Ant Design表单联动验证:字段依赖与关联校验 在企业级应用开发中,表单往往包含复杂的字段关联关系,例如"确认密码"需与"密码"字段匹配,"自定义金额"需
UI组件前端设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考