如何为Blazor动态生成字段实现远程验证?
Blazor动态生成字段的远程验证实现方案
针对动态生成的邮箱输入框实现远程验证,核心是通过自定义异步验证属性结合EditContext的字段级验证来实现,以下是完整的落地方案:
1. 定义带验证的模型类
首先创建一个包装单个邮箱的模型,集成内置验证和自定义远程验证规则:
public class EmailItem { [Required(ErrorMessage = "邮箱不能为空")] [EmailAddress(ErrorMessage = "请输入有效的邮箱格式")] [RemoteEmailExists(ErrorMessage = "该邮箱已存在")] public string Email { get; set; } }
2. 实现自定义异步远程验证属性
继承ValidationAttribute并实现IAsyncValidator接口,完成后端API调用逻辑:
using Microsoft.AspNetCore.Components.Forms; using System.Net.Http.Json; public class RemoteEmailExistsAttribute : ValidationAttribute, IAsyncValidator { public async Task<IEnumerable<ValidationResult>> ValidateAsync( ValidationContext validationContext, CancellationToken cancellationToken) { var emailItem = validationContext.ObjectInstance as EmailItem; if (emailItem == null || string.IsNullOrWhiteSpace(emailItem.Email)) return Enumerable.Empty<ValidationResult>(); // 从验证上下文注入HttpClient var httpClient = validationContext.GetRequiredService<HttpClient>(); // 调用后端API检查邮箱是否存在 var exists = await httpClient.GetFromJsonAsync<bool>( $"api/emails/check-exists?email={Uri.EscapeDataString(emailItem.Email)}", cancellationToken); return exists ? new[] { new ValidationResult(ErrorMessage, new[] { nameof(EmailItem.Email) }) } : Enumerable.Empty<ValidationResult>(); } }
3. 封装动态输入组件
创建可复用的动态输入组件,绑定单个EmailItem并处理验证触发:
@using Microsoft.AspNetCore.Components.Forms <div class="mb-3"> <InputText @bind-Value="Item.Email" class="form-control" @oninput="TriggerValidation" /> <ValidationMessage For="@FieldIdentifier" class="text-danger mt-1" /> </div> @code { [Parameter, Required] public EmailItem Item { get; set; } [CascadingParameter] private EditContext EditContext { get; set; } private FieldIdentifier FieldIdentifier => FieldIdentifier.Create(Item, nameof(Item.Email)); private async Task TriggerValidation(ChangeEventArgs e) { // 触发当前字段的异步验证 await EditContext.ValidateFieldAsync(FieldIdentifier); // 通知字段状态变更,更新UI EditContext.NotifyFieldChanged(FieldIdentifier); } }
4. 页面集成与提交控制
在主页面中渲染动态字段,绑定EditForm并控制提交状态:
@page "/dynamic-email-validation" @inject HttpClient HttpClient <EditForm Model="@EmailItems" OnValidSubmit="HandleSubmit" class="container mt-4"> <DataAnnotationsValidator /> <ValidationSummary class="alert alert-warning mb-3" /> @foreach (var item in EmailItems) { <DynamicEmailInput Item="@item" /> } <button type="button" class="btn btn-secondary me-2" @onclick="AddEmailField">添加邮箱</button> <button type="submit" class="btn btn-primary" disabled="@(!IsFormValid)">提交</button> </EditForm> @code { private List<EmailItem> EmailItems { get; set; } = new(); private bool IsFormValid => EditContext?.Validate() ?? false; protected override void OnInitialized() { // 初始化一个默认输入框 EmailItems.Add(new EmailItem()); } private void AddEmailField() { EmailItems.Add(new EmailItem()); StateHasChanged(); } private async Task HandleSubmit() { // 提交所有邮箱到后端 var emails = EmailItems.Select(x => x.Email).ToList(); await HttpClient.PostAsJsonAsync("api/emails/save", emails); // 提交后的逻辑,比如清空或提示 EmailItems.Clear(); EmailItems.Add(new EmailItem()); } }
后端API示例(可选)
对应的后端检查接口可以这样实现(ASP.NET Core控制器):
[ApiController] [Route("api/emails")] public class EmailsController : ControllerBase { // 模拟数据库存储的邮箱列表 private static readonly List<string> ExistingEmails = new() { "test@example.com", "user@domain.com" }; [HttpGet("check-exists")] public IActionResult CheckExists(string email) { var exists = ExistingEmails.Contains(email, StringComparer.OrdinalIgnoreCase); return Ok(exists); } [HttpPost("save")] public IActionResult Save([FromBody] List<string> emails) { // 处理保存逻辑 return Ok(); } }
关键说明
- 每个动态字段绑定独立的
EmailItem实例,确保验证状态互不干扰; - 通过
EditContext.ValidateFieldAsync触发单个字段的异步验证,避免全表单重验证; - 提交按钮的禁用状态通过
EditContext.Validate()实时判断,确保表单无效时无法提交。
内容的提问来源于stack exchange,提问作者Sagar Khatri
相关产品推荐
相关产品推荐

