MVC,子实体,编辑器,和不显眼的验证
本文关键字:不显眼 验证 编辑器 实体 MVC | 更新日期: 2023-09-27 17:49:42
这可能会是一个巨大的…
好的,所以我正在建立一个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'"标签来模仿这种行为(在示例中没有显示,我还没有这样做),这对我来说似乎非常混乱。
所以我有了一个绝妙的想法,采用内置模板并创建我自己的模板来注入这些东西(以及我自己的一些其他东西,例如bootstrap的"占位符"属性用于"helper"文本,也许还有工具提示等)。我下载了MVC源代码并打开了默认的编辑器模板,但是我没有看到呈现不显眼值的标记,而是得到了一大堆在某个时刻"神奇地"呈现不显眼属性的帮助函数。我不知道它是如何完成的,因为验证的东西都打包到内部类,我不能访问。
我在这里错过了什么,或者这只是MVC的一个弱点,我将不得不解决。我真的很喜欢不需要自己模拟不显眼的验证属性生成代码,但如果这是唯一的解决方案,我想我可以这样做…
谢谢!
我花了一个下午的时间对源代码进行了更多的研究,并发现了几个关键的帮助方法,这些方法允许我做我需要做的事情。
这不是世界上最漂亮的东西,但它对自动化我以前所做的事情有很大帮助。我的解决方案是创建一个新的TextBoxFor辅助方法,名为TextBoxForChild:
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"的值,称为"Prompt":
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; }
}
可爱!