MVC,子实体,EditorFor和Unobtrusive验证

时间:2012-12-28 17:03:12

标签: c# .net asp.net-mvc-4 unobtrusive-validation razor-2

这可能会成为一个巨大的......

好的,所以我正在建立一个MVC4网站,该网站具有中等频率的主题,能够编辑父记录以及在同一页面上添加/删除/编辑子记录。使用MVC的魔力,我可以为子记录定义部分视图,如下所示:

@model NFBC.Models.SubMap

<tr id="@String.Concat("SubMap", ViewBag.Index)">
    <td class="mapname">
        <input type="hidden" name="submaps[@ViewBag.Index].Id" value="@Model.Id" />
        <input type="text" name="submaps[@ViewBag.Index].MapName" value="@Model.MapName" />
    </td>
    <td class="miles"><input type="text" name="submaps[@ViewBag.Index].Miles" value="@Model.Miles" /></td>
    <td class="difficulty">@Html.DropDownList("submaps[" + (string)ViewBag.Index.ToString() + "].DifficultyId", (SelectList)ViewBag.Difficulty(Model.DifficultyId))</td>
    <td class="elevation"><input type="text" name="submaps[@ViewBag.Index].Elevation" value="@Model.Elevation" /></td>
    <td class="mapfile"><input type="text" name="submaps[@ViewBag.Index].MapFile" value="@Model.MapFile" /></td>
    <td class="delete"><img src="~/Images/Error_red_16x16.png" /></td>
</tr>

然后在父视图中,我只需调用局部视图来渲染其所有子节点:

<table id="ChoicesTable">
    <thead>
        <tr>
            <th>Map Name</th>
            <th>Miles</th>
            <th>Difficulty</th>
            <th>Elevation</th>
            <th>Map File</th>
            <th></th>
        </tr>
    </thead>
    for (int i = 0; i < Model.SubMaps.Count; i++)
    {
        var map = Model.SubMaps.ElementAt(i);
        ViewBag.Index = i;
        Html.RenderPartial("_MapChoiceEditRow", map);
    }
</table>

我无法找到关于“子实体”名称语法的任何文档(即:name =“subMaps [@ ViewBag.Index] .Id”),但它在绑定到模型时有效;只要索引值从0开始并且没有丢失任何值(即:0,1,2,4,5将导致仅0,1和2的绑定),所有子项都被填充。使用jQuery的Ajax调用的魔力,我能够在客户端动态插入和删除行。

问题在于我根本无法找到一种方法可靠地将@ Html.EditorFor()与子实体控件一起使用。这将是非常好的功能,因为EditorFor将所有不显眼的jquery验证属性注入到html中。现在我基本上被迫通过添加我自己的“data-val ='true'”标签来模仿这种行为(示例中没有显示,我还没有完成)到处都是,这对我来说似乎非常凌乱

所以我有一个很棒的想法,即使用内置模板并创建我自己的模板来注入这些东西(以及我自己的一些其他东西,例如Bootstraps“占位符”属性的“帮助”文本,以及也许是工具提示等)。我下载了MVC源并打开了默认的编辑器模板,但是我没有看到渲染不显眼的值的标记,而是获得了一大堆辅助函数,在某些时候“神奇地”渲染了不显眼的属性。我无法弄清楚它是如何完成的,因为验证内容都被打包到我无法访问的内部类中。

我在这里遗漏了什么,或者这只是MVC的一个弱点,我将不得不解决这个问题。我真的很想不要自己模仿不显眼的验证属性生成代码,但如果它是唯一的解决方案,我想我可以做到......

谢谢!

1 个答案:

答案 0 :(得分:2)

我花了一个下午的时间来讨论源代码,并发现了几个关键的辅助方法,让我可以做我需要做的事情。

这不是世界上最漂亮的东西,但它对于实现我之前所做的事情的自动化还有很长的路要走。我的解决方案是创建一个名为TextBoxForChild的新TextBoxFor辅助方法:

    public static MvcHtmlString TextBoxForChild<TModel, TProperty>(
            this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression,
            string parentName,
            int index,
            IDictionary<string, object> htmlAttributes)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        var rules = ModelValidatorProviders.Providers.GetValidators(metadata, htmlHelper.ViewContext).SelectMany(v => v.GetClientValidationRules());

        if (htmlAttributes == null)
            htmlAttributes = new Dictionary<String, object>();
        if (!String.IsNullOrWhiteSpace(metadata.Watermark))
            htmlAttributes["placeholder"] = metadata.Watermark;
        UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(rules, htmlAttributes);

        return htmlHelper.TextBox(String.Format("{0}[{1}].{2}", parentName, index, metadata.PropertyName), metadata.Model, htmlAttributes);
    }

我得到“ModelMetadata”对象,然后我为模型生成“模型验证器规则”。然后我用水印元数据值填写我的自定义“占位符”html属性,最后调用“UnobtrusiveValidationAttributesGenerator.GetValidationAttributes”,它用我的html属性字典填充所有验证属性。

然后我做一些自定义字符串格式化以确保输入名称遵循MVC子实体格式,瞧,它有效!

如果有人想知道“水印”值来自何处,它是来自“DisplayAttribute”的值,称为“提示”:

public class FamilyMember
{
    public int ClubNumber { get; set; }

    [Display(Name="Name", Prompt="Name")]
    [Required]
    public string Name { get; set; }
    public string Cell { get; set; }
    public string Email1 { get; set; }
    public string Email2 { get; set; }
    public DateTime? BirthDate { get; set; }
}

可爱!