我正在实现一个MVC3 / Razor Web应用程序,它可以检索用户可以从其他服务编辑的一些“字段”,因此在视图中编辑的属性列表在编译时是完全动态的和未知的。
我编写了一些部分视图和HTML帮助程序,它们遍历从其他服务检索到的组和属性。现在,我必须为各种属性类型构建标记,并考虑为什么不重新使用MVC编辑器模板系统呢?支持各种数据类型(例如复选框等),甚至可以使用我的自定义模板对其进行自定义。
到目前为止一切顺利,但如何将Html.EditorFor()
或Html.Editor()
用于自定义数据对象/属性?构建动态表单而不使用静态类型数据作为视图模型的含义。
以下是我的HTML帮助程序代码的简约示例:
public static MvcHtmlString GetField(this HtmlHelper helper, Field field)
{
...
return helper.EditorFor(field, m => m.Value);
...
}
属性“field”是我从外部服务获得的字段。它具有object类型的“Value”属性。我喜欢为这个属性类型构建编辑器代码。
据我所知,编辑器模板是基于当前视图模型构建的。我可以将另一个对象作为模型传递给当前视图模型(例如,在上面的示例“field”中)?
任何帮助都会很棒!
干杯, 马克
答案 0 :(得分:3)
我的第一个ASP MVC任务涉及构建动态表单,而且不是直截了当的 基本上你不能使用内置的助手或验证,因为他们期望强类型的对象。
我的视图基本上遍历输入字段,检查DataType(bool,int,string,datetime等)并直接为该类型构建编辑器。
您还必须手动执行所有验证,使用属性修改类型以执行此操作不起作用,请参阅我的问题if(ModelState.IsValid) doesn't work with FormsCollection. What to use instead?
Razor View逻辑(我们使用DevExpress MVC扩展,但你得到漂移)
表单对象及其字段集合是我们定制的对象,它描述了表单的外观(页面收集搜索条件,这就是为什么你会在代码中看到标准和搜索类型名称)。
<table id="criteriaTable">
@foreach (var field in form.Fields)
{
<tr id="criteriaTableRow">
@if (field.IsVisible)
{
<td id="criteriaTableLabelCol">
@Html.DevExpress().Label(s => s.Text = field.Caption).GetHtml()
</td>
<td id="criteriaTableEditCol">
@if (field.Type == typeof(bool))
{
@Html.CheckBox(s =>
{
s.Checked = field.IsBoolSet;
s.Name = field.Name;
s.ClientEnabled = !field.IsReadonly;
}).GetHtml()
}
else if (field.Type == typeof(DateTime))
{
Html.DevExpress().DateEdit(s =>
{
s.Name = field.Name;
s.ClientEnabled = !field.IsReadonly;
if (!string.IsNullOrEmpty(field.Value))
{
DateTime dateValue;
if (DateTime.TryParse(field.Value, out dateValue))
s.Date = dateValue;
}
}).GetHtml();
}
else if (field.ListValues.Count > 0)
{
<input type="hidden" id="@("initiallySelected" + field.Name)" value="@field.Value" />
Html.DevExpress().ListBox(s =>
{
s.Name = field.Name;
s.ClientVisible = field.IsVisible;
s.ClientEnabled = !field.IsReadonly;
s.Properties.SelectionMode = DevExpress.Web.ASPxEditors.ListEditSelectionMode.CheckColumn;
s.Properties.TextField = "Name";
s.Properties.ValueField = "Value";
s.Properties.ValueType = typeof(string);
//s.Properties.EnableClientSideAPI = true;
foreach (var item in field.ListValues)
{
s.Properties.Items.Add(item.Name, item.Value);
}
//s.Properties.ClientSideEvents.SelectedIndexChanged = "MultiSelectListChanged";
s.Properties.ClientSideEvents.Init = "MultiSelectListInit";
}).GetHtml();
}
else
{
//Html.TextBox(field.Name, field.Value)
Html.DevExpress().TextBox(s =>
{
s.Name = field.Name; s.Text = field.Value;
}).GetHtml();
}
@Html.ValidationMessage(field.Name)
<input type="hidden" name="@("oldvalue_" + field.Name)" value="@field.Value" />
<input type="hidden" name="@("olduse_" + field.Name)" value="@(field.IncludeInSearch ? "C" : "U")" />
</td>
<td id="criteriaTableIncludeCol">
@Html.DevExpress().CheckBox(s =>
{
s.Checked = field.IncludeInSearch;
s.Name = "use_" + field.Name;
s.ClientEnabled = (!field.IsMandatory);
}).GetHtml()
</td>
}
</tr>
}
</table>
Controller操作接受Forms Collection。我循环遍历formsCollection,查找我在视图中指定的控件名称。
[HttpPost]
public ActionResult QueryCriteria(FormCollection formCollection)
{
var isValid = true;
foreach (var field in form.Fields)
{
var value = (formCollection[field.Name] ?? "").Trim();
...
如果存在任何验证错误,我可以通过将ModelError直接添加到模型来指定控制级别验证,例如
ModelState.AddModelError(field.Name, "This is a mandatory field");
如果有验证错误,我会返回View。
希望这有帮助。