如何验证多个通过Ajax动态加载的Partial Views?
动态加载分部视图的验证解决方案
遇到动态加载分部视图的验证问题很常见,因为jQuery Unobtrusive Validate默认只会在DOM首次加载时绑定验证规则,动态添加的元素不会自动被纳入验证范围。下面是针对你的场景的完整实现方案:
1. 确保分部视图包含完整的验证标记
首先,你的Caller、Child和CallNote分部视图必须正确生成验证相关的HTML标记,这样客户端验证才能识别。以Caller分部视图为例:
@model CallerViewModel <div class="well" id="caller-@Model.Id"> @Html.HiddenFor(m => m.Id) @Html.HiddenFor(m => m.CallDetailId) <div class="form-group"> @Html.LabelFor(m => m.FullName, new { @class = "control-label" }) @Html.ValidationMessageFor(m => m.FullName, "", new { @class = "text-danger" }) @Html.TextBoxFor(m => m.FullName, new { @class = "form-control", required = "required" }) </div> <!-- 其他Caller属性的输入框及验证消息 --> <button type="button" class="btn btn-danger delete-caller" data-callerid="@Model.Id">Delete Caller</button> </div>
关键:每个需要验证的属性都要搭配@Html.ValidationMessageFor,这是客户端验证显示错误的载体。
2. 动态加载后重新解析验证规则
每次AJAX加载完分部视图后,需要手动触发jQuery Unobtrusive Validate重新解析新元素的验证规则。修改你的addWhateverPartialView函数:
function addWhateverPartialView(divToReplace, thingIWantId, callDetailId, type) { let url = ''; switch(type) { case 'caller': url = '/Home/GetCallerPartialViewAsync'; break; case 'child': url = '/Home/GetChildPartialViewAsync'; break; case 'callNote': url = '/Home/GetCallNotePartialViewAsync'; break; } $.ajax({ url: url, data: { thingIWantId: thingIWantId, callDetailId: callDetailId }, type: "GET", error: function (xmlHttpRequest, textStatus, errorThrown) { alert("Request: " + xmlHttpRequest.toString() + "\n\nStatus: " + textStatus + "\n\nError: " + errorThrown); }, success: function (data) { const $target = $(divToReplace); $target.replaceWith(data); // 1. 解析新添加元素的验证规则 const $newContent = $target.next(); $.validator.unobtrusive.parse($newContent); // 2. 重置表单验证器,确保新元素被纳入全局验证范围 const $form = $('form'); $form.data('validator', null); $.validator.unobtrusive.parse($form); } }); }
为什么要重置表单验证器? 原表单的验证实例在DOM就绪时就已创建,不会自动感知新添加的元素,重置后重新解析才能让所有元素(包括动态加载的)参与验证。
3. 服务器端验证子集合
客户端验证只是第一道防线,必须配合服务器端验证确保数据合法性。在SubmitCallDetailsAsync方法中,要确保子视图模型的每个属性都被验证:
[HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> SubmitCallDetailsAsync(CallDetailViewModel callDetailViewModel) { using (var unitOfWork = new UnitOfWork(ApplicationDbContext)) { // 手动验证Caller集合(如果默认模型绑定未覆盖的场景) for (int i = 0; i < callDetailViewModel.CallerViewModels.Count; i++) { var caller = callDetailViewModel.CallerViewModels[i]; if (string.IsNullOrWhiteSpace(caller.FullName)) { ModelState.AddModelError($"CallerViewModels[{i}].FullName", "Caller name is required"); } // 其他Caller属性的验证逻辑 } // 手动验证Child集合 for (int i = 0; i < callDetailViewModel.ChildViewModels.Count; i++) { var child = callDetailViewModel.ChildViewModels[i]; if (string.IsNullOrWhiteSpace(child.FirstName)) { ModelState.AddModelError($"ChildViewModels[{i}].FirstName", "Child first name is required"); } // 其他Child属性的验证逻辑 } if (!ModelState.IsValid) { // 返回原视图,显示所有验证错误 return View("Contact", callDetailViewModel); } // 后续保存逻辑 var callDetailServices = new CallDetailServices(); await callDetailServices.AddOrUpdateCallDetailFromCallDetailViewModelAsync(callDetailViewModel, ModelState); return RedirectToAction("Index"); } }
说明:如果你的视图模型已经使用了DataAnnotation特性(如[Required]),默认模型绑定会自动验证,但复杂场景下(比如依赖其他属性的验证)需要手动添加错误到ModelState。
4. 删除元素后的验证清理
当用户删除Caller或Child时,需要移除对应的DOM元素并更新表单验证规则:
// 监听删除Caller按钮点击 $(document).on('click', '.delete-caller', function() { const callerId = $(this).data('callerid'); $(`#caller-${callerId}`).remove(); // 更新表单验证规则,移除已删除元素的验证逻辑 const $form = $('form'); $form.data('validator', null); $.validator.unobtrusive.parse($form); }); // 同理实现删除Child的逻辑 $(document).on('click', '.delete-child', function() { const childId = $(this).data('childid'); $(`#child-${childId}`).remove(); const $form = $('form'); $form.data('validator', null); $.validator.unobtrusive.parse($form); });
5. 测试验证流程
完成以上修改后,测试以下场景:
- 动态加载Caller/Child后,输入不符合规则的内容,点击Submit按钮,客户端应实时显示错误
- 提交不符合规则的表单,服务器端应返回原视图并显示所有错误
- 删除某个Caller/Child后,提交表单不会再验证已删除的元素
内容的提问来源于stack exchange,提问作者Ryan Taite
相关产品推荐
相关产品推荐

