MVC4如何动态地将行项添加到EditorFor字段?

我有一个视图模型女巫包含迭代项目。 我通过EditorFor()方法将它们放在我的视图中。

视图:

@model Models.MyModel @using (Html.BeginForm(@Model.Action, @Model.Controller)) { 
@Html.EditorFor(m => m.Terms)
}

模型:

 public class MyModel { public IEnumerable Terms { get; set; } } 

EditorTemplates \ Term.cshtml:

 @model Models.Term @if (Model != null) { 
Term @Html.HiddenFor(model => model.TermID)
@Html.LabelFor(model => model.Identifier)
@Html.EditorFor(model => model.Identifier) @Html.ValidationMessageFor(model => model.Identifier)
@Html.LabelFor(model => model.Description)
@Html.EditorFor(model => model.Description) @Html.ValidationMessageFor(model => model.Description)
}

我希望能够在视图中动态添加/删除列表中的项目,比如knockout.js上的这个示例,但是如何保留auto-id的MVC创建??:

http://knockoutjs.com/examples/cartEditor.html

以下是我对此的要求:

  • 添加新条款
  • 删除条款
  • validation添加的新术语视图

我已经阅读了有关SO的其他问题,但我还没有找到真正明确的答案。 knockout.js是可以接受的方式吗? 是否有任何使用Knockout和MVC执行此操作的示例?

谢谢!

你想淘汰MVC http://knockoutmvc.com/CartEditor

您不必为此使用knockout,您真正需要的是具有validation的javascript和创建/删除操作,这些操作映射到MVC方面的静止控制器操作。 你如何实现这一点取决于你。 Knockout让它变得简单。

我发现这个post是由Jarrett Meyer 在MVC3中发布的嵌套集合模型 ,他有一个不使用knockout并最大化代码重用的解决方案。

这包括添加和删除方法。 我将在这里概述添加方法。

该模型

 public class Person { public string FirstName { get; set; } public string LastName { get; set; } public IList PhoneNumbers { get; set; } public IList EmailAddresses { get; set; } public IList
Addresses { get; set; } }

查看

 //New.cshtml: @using (Html.BeginForm("New", "Person", FormMethod.Post)) { @Html.EditorForModel() 

} //Person.cshtml: @Html.AntiForgeryToken() @Html.HiddenFor(x => x.Id)

@Html.TextBoxFor(x => x.FirstName)

@Html.TextBoxFor(x => x.LastName)

@Html.EditorFor(x => x.PhoneNumbers)

@Html.LinkToAddNestedForm("Add Phone Number", "#phoneNumbers", ".phoneNumber", "PhoneNumbers", typeof(PhoneNumber))

//PhoneNumber.cshtml:

@Html.TextBoxFor(x => x.Number)


帮手

 /// Text for Link /// where this block will be inserted in the HTML using a jQuery append method /// name of the class inserting, used for counting the number of items on the form /// the prefix that needs to be added to the generated HTML elements /// The type of the class you're inserting public static IHtmlString LinkToAddNestedForm(this HtmlHelper htmlHelper, string linkText, string containerElement, string counterElement, string collectionProperty, Type nestedType) { var ticks = DateTime.UtcNow.Ticks; var nestedObject = Activator.CreateInstance(nestedType); var partial = htmlHelper.EditorFor(x => nestedObject).ToHtmlString().JsEncode(); partial = partial.Replace("id=\\\"nestedObject", "id=\\\"" + collectionProperty + "_" + ticks + "_"); partial = partial.Replace("name=\\\"nestedObject", "name=\\\"" + collectionProperty + "[" + ticks + "]"); var js = string.Format("javascript:addNestedForm('{0}','{1}','{2}','{3}');return false;", containerElement, counterElement, ticks, partial); TagBuilder tb = new TagBuilder("a"); tb.Attributes.Add("href", "#"); tb.Attributes.Add("onclick", js); tb.InnerHtml = linkText; var tag = tb.ToString(TagRenderMode.Normal); return MvcHtmlString.Create(tag); } private static string JsEncode(this string s) { if (string.IsNullOrEmpty(s)) return ""; int i; int len = s.Length; StringBuilder sb = new StringBuilder(len + 4); string t; for (i = 0; i < len; i += 1) { char c = s[i]; switch (c) { case '>': case '"': case '\\': sb.Append('\\'); sb.Append(c); break; case '\b': sb.Append("\\b"); break; case '\t': sb.Append("\\t"); break; case '\n': //sb.Append("\\n"); break; case '\f': sb.Append("\\f"); break; case '\r': //sb.Append("\\r"); break; default: if (c < ' ') { //t = "000" + Integer.toHexString(c); string tmp = new string(c, 1); t = "000" + int.Parse(tmp, System.Globalization.NumberStyles.HexNumber); sb.Append("\\u" + t.Substring(t.Length - 4)); } else { sb.Append(c); } break; } } return sb.ToString(); } 

使用Javascript

 //since the html helper can change the text of the item inserted but not the index, //this replaces the 'ticks' with the correct naming for the collection of properties function addNestedForm(container, counter, ticks, content) { var nextIndex = $(counter).length; var pattern = new RegExp(ticks, "gi"); content = content.replace(pattern, nextIndex); $(container).append(content); } 

您需要执行以下操作:

  1. 将模型发送到控制器并为术语渲染一个可编辑字段。
  2. 您要求用户提交此内容或单击“添加”以添加更多字词。
  3. 如果用户点击添加,您可以创建现有字段的副本并清空它们或其他任何内容以便创建新字段。
  4. 当用户提交时,您将其发送回接受一系列术语并将其添加到数据库或不添加的操作。 或者您可以使用上面示例中的ajax,或者从此示例中可以看到它发送给服务器的内容是json数组对象而不是带有命名元素的表单。
  5. 您可以在创建时处理它们,也可以在提交时处理它们。

取决于您的应用程序,所以淘汰赛只是帮助您在客户端进行第3步,您将从旧副本创建新的。 您也可以为此使用JQuery模板,并使用旧的浏览器支持对json2进行序列化。

您需要了解的是,一旦您在客户端,您已经将模型发送到此处,因此不必担心服务器端。 无论你在客户端构建什么,都可以一次发送一个模型来saveTerm动作,它使用术语id和其他信息重新生成json,或者你可以将saveTerm的集合作为json数组返回,它可以正常工作。

如果你想在postBack上发送数组而不是ajax,只需保持表单元素名称相同并重复术语输入字段并发送。 MVC会将它们映射到与json一样的术语数组。