当我在视图上只有一个模型实例时,远程验证工作正常。
问题在于我的观点是处理模型集合。这是我的模特:
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="&#39;Name&#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="&#39;Name&#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将被选中。
答案 0 :(得分:1)
首先,是的,您可以改进视图的语法。使用 EditorTemplates 。
创建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
...而且默认的模型绑定器实际上并不知道如何处理它。
我建议您编写自己的自定义模型绑定器。告诉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
方法使用此模型绑定器:
public JsonResult IsNameAvailable([ModelBinder(typeof(JsonGridRowModelBinder))] TableFormTestModel.GridRow gridRow) {
...
}