You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证多个通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:25:27