Skip to main content
 首页 » 编程设计

jquery之使用附加字段对模型集合进行远程验证

2026年07月31日6bjzhanghao

当 View 上只有一个模型实例时,远程验证工作正常。

问题是当我的 View 处理模型集合时。这是我的模型:

public class TableFormTestModel 
{ 
    public GridRow[] GridData { get; set; } 
    public class GridRow 
    { 
        public Int32 Id { get; set; } 
 
        [Required, StringLength(50), Remote("IsNameAvailable", "TableFormTest", "Admin", AdditionalFields = "Id")] 
        public String Name { get; set; } 
    } 
} 

在我看来,我有:

@model TableFormTestModel 
@using (Html.BeginForm()) 
{ 
    Html.EnableClientValidation(); 
    Html.EnableUnobtrusiveJavaScript(); 
    for(var i = 0;i<Model.GridData.Length;i++) 
    { 
    <div> 
        @Html.HiddenFor(x => Model.GridData[i].Id) 
        @Html.TextBoxFor(x => Model.GridData[i].Name) 
        @Html.ValidationMessageFor(x => Model.GridData[i].Name)     
    </div> 
    } 
} 

生成表单的方法很长,有人可以帮我改进语法吗?

生成以下 html 表单:

<form method="post" action="/Admin/TableFormTest/">    <div> 
    <input type="hidden" value="1" name="GridData[0].Id" id="GridData_0__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true"> 
    <input type="text" value="abc" name="GridData[0].Name" id="GridData_0__Name" data-val-required="The Name field is required." data-val-remote-url="/Admin/TableFormTest/IsNameAvailable" data-val-remote-additionalfields="*.Name,*.Id" data-val-remote="&amp;#39;Name&amp;#39; is invalid." data-val-length-max="50" data-val-length="The field Name must be a string with a maximum length of 50." data-val="true"> 
    <span data-valmsg-replace="true" data-valmsg-for="GridData[0].Name" class="field-validation-valid"></span>     
</div> 
<div> 
    <input type="hidden" value="2" name="GridData[1].Id" id="GridData_1__Id" data-val-required="The Id field is required." data-val-number="The field Id must be a number." data-val="true"> 
    <input type="text" value="def" name="GridData[1].Name" id="GridData_1__Name" data-val-required="The Name field is required." data-val-remote-url="/Admin/TableFormTest/IsNameAvailable" data-val-remote-additionalfields="*.Name,*.Id" data-val-remote="&amp;#39;Name&amp;#39; is invalid." data-val-length-max="50" data-val-length="The field Name must be a string with a maximum length of 50." data-val="true"> 
    <span data-valmsg-replace="true" data-valmsg-for="GridData[1].Name" class="field-validation-valid"></span>     
</div> 

虽然上面的 html 看起来相当不错(集合中的每个模型都有唯一的 id 和名称),但远程验证的附加字段存在问题:

data-val-remote-additionalfields="*.Name,*.Id" 

当在第二行上触发远程验证时,会获取第一行的 ID。

请您参考如下方法:

首先,是的,您可以改进 View 的语法。使用编辑器模板。

创建Views\Shared\EditorTemplates\GridRow.cshtml:

@model TestMvc.Models.TableFormTestModel.GridRow 
<div> 
    @Html.HiddenFor(x => x.Id) 
    @Html.TextBoxFor(x => x.Name) 
    @Html.ValidationMessageFor(x => x.Name) 
</div> 

现在您的主视图只需:

@model TableFormTestModel 
@using (Html.BeginForm()) 
{ 
    Html.EnableClientValidation(); 
    Html.EnableUnobtrusiveJavaScript(); 
    @Html.EditorFor(x => x.GridData) 
} 
<小时 />

至于RemoteAttribute的麻烦,就比较棘手了。该问题是由 MVC 为数组创建的输入名称引起的。如您所见,您的输入已命名,例如 GridData[1].Id、GridData[1].Name (等)。好吧,jQuery 通过将这些名称提供给查询字符串来进行 ajax 调用。

因此,最终被调用的是

/Admin/TableFormTest/IsNameAvailable?GridData%5B1%5D.Name=sdf&GridData%5B1%5D.Id=5  

又名

/Admin/TableFormTest/IsNameAvailable?GridData[1].Name=sdf&GridData[1].Id=5  

...默认的模型绑定(bind)器确实不知道如何处理它。

我的建议是编写您自己的自定义模型绑定(bind)器。告诉 MVC 如何读取这个查询字符串,然后创建你想要的对象。

这是一个概念验证。 (但不要在生产中使用这个东西:它需要太多假设,并且会因任何意外而崩溃。)

public class JsonGridRowModelBinder : IModelBinder { 
 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { 
        var model = new TableFormTestModel.GridRow(); 
        var queryString = controllerContext.HttpContext.Request.QueryString; 
        model.Name = queryString[queryString.AllKeys.Single(x => x.EndsWith("Name"))]; 
        string id = queryString[queryString.AllKeys.Single(x => x.EndsWith("Id"))]; 
        model.Id = string.IsNullOrWhiteSpace(id) ? 0 : int.Parse(id); 
 
        return model; 
    } 
 
} 

然后,告诉您的 IsNameAvailable 方法使用此模型绑定(bind)器:

public JsonResult IsNameAvailable([ModelBinder(typeof(JsonGridRowModelBinder))] TableFormTestModel.GridRow gridRow) { 
    ... 
}