如何在ASP.NET MVC中实现提交若干个某模型的数据(某Model的List或ICollection,大小不定)
2014-12-14 13:21
696 查看
背景说明
在ASP.NET MVC中,有一个我们经常使用且十分好用的功能——模型绑定。即在页面中指定该页面将会使用到的数据模型Model,然后在“显示数据”或“提交数据”时,就可以很方便的获取数据内容。
通常情况下这两种场景都比较容易实现,特别是“显示数据”时,不管绑定的模型如何复杂都可以轻松的显示所有你想要的数据。
但是,“提交数据”时,就不一定那么简单了。特别是当模型中包含一个List或者ICollection数据(即实现集合类型接口的数据),且该数据中包含的item数量不确定时,情况就变的有些棘手了。
问题描述
下面举一个这种情况的例子假如要给一个系统录入一个某业务的操作说明页面,页面包含如下内容:1). 标题 2). 业务介绍 3). 操作步骤(录入前不确定多少步)
解决方案
因此,这个页面对应着如下模型 BusinessHelppublic class BusinessHelp { public string BusinessTitle{ get; set; } public string BusinessDescription{ get; set; } public virtual ICollection<StepDetail> StepDetails { get; set; } }
在录入页面AddBusinessHelp.cshtml中,主要内容如下:
@model HospitalProject.Models.AdminModel.BusinessHelp @using (Html.BeginForm("ActionName","ControllerName", FormMethod.Post)) { @Html.TextBoxFor(m => m.BusinessTitle) @Html.TextAreaFor(m => m.BusinessDescription) <div id="movieEditor"> @foreach (StepDetail step in Model.StepDetails) { Html.RenderPartial("StepDetailItem", step); } </div> <button id="#addAnother">添加步骤</button> }
其中StepDetailItem的PartialView如下:
@model HospitalProject.Models.StepDetail @using (Html.BeginCollectionItem("StepDetails")) { @Html.TextBoxFor(m => m.Title) @Html.TextAreaFor(m => m.Content) }
对应这个视图的Action中的代码如下:
private static BusinessHelp _currentBusiness; private static BusinessHelp CurrentBusiness { get { if (_currentBusiness == null) _currentBusiness = GetBusiness(); return _currentBusiness; } set { _currentBusiness = value; } } private static BusinessHelp GetBusiness() { return new BusinessHelp { BusinessTitle = "", BusinessDescription = "", StepDetails = new List<StepDetail>() { new StepDetail() { Title = "", Content = "", PageName = ""}, } }; } public ActionResult AddBusinessHelp() { return View(CurrentBusiness); }
每次给视图传递一个“空内容”的Model。(只是实际字符串等内容为空,数据模型对象并不为null)
这样每次进行业务帮助页面的录入时,都会初始化第一步。后续再想添加第二部、第三部 ......都可以通过js添加,并绑定到模型(稍后提到~)
在AddBusinessHelp.cshtml中 添加如下js代码:
$(function () { $("#addAnother").click(function () { $.get('/Help/StepEntryRow?count='+(++count), function (template) { $("#movieEditor").append(template); }); }); }
其中StepEntryRow Action代码如下:
public ActionResult StepEntryRow(int count = -1) { if (count != -1) { ViewBag.StepNumber = count; StepDetail sd = new StepDetail() { Title = "", Content = "", Type = Models.EnumModel.StepType.Detail }; return PartialView("StepDetailItem", sd); } else return View("Error"); }
这样就基本完成了该功能的实现了。
下面贴出上述代码中的核心部分—— Html.BeginCollectionItem("StepDetails")
public static class CollectionEditingHtmlExtensions { /// <summary> /// Begins a collection item by inserting either a previously used .Index hidden field value for it or a new one. /// </summary> /// <typeparam name="TModel"></typeparam> /// <param name="html"></param> /// <param name="collectionName">The name of the collection property from the Model that owns this item.</param> /// <returns></returns> public static IDisposable BeginCollectionItem<TModel>(this HtmlHelper<TModel> html, string collectionName) { if (String.IsNullOrEmpty(collectionName)) throw new ArgumentException("collectionName is null or empty.", "collectionName"); string collectionIndexFieldName = String.Format("{0}.Index", collectionName); string itemIndex = null; if (html.ViewData.ContainsKey(JQueryTemplatingEnabledKey)) { itemIndex = "${index}"; } else { itemIndex = GetCollectionItemIndex(collectionIndexFieldName); } string collectionItemName = String.Format("{0}[{1}]", collectionName, itemIndex); TagBuilder indexField = new TagBuilder("input"); indexField.MergeAttributes(new Dictionary<string, string>() { { "name", collectionIndexFieldName }, { "value", itemIndex }, { "type", "hidden" }, { "autocomplete", "off" } }); html.ViewContext.Writer.WriteLine(indexField.ToString(TagRenderMode.SelfClosing)); return new CollectionItemNamePrefixScope(html.ViewData.TemplateInfo, collectionItemName); } private const string JQueryTemplatingEnabledKey = "__BeginCollectionItem_jQuery"; public static MvcHtmlString CollectionItemJQueryTemplate<TModel, TCollectionItem>(this HtmlHelper<TModel> html, string partialViewName, TCollectionItem modelDefaultValues) { ViewDataDictionary<TCollectionItem> viewData = new ViewDataDictionary<TCollectionItem>(modelDefaultValues); viewData.Add(JQueryTemplatingEnabledKey, true); return html.Partial(partialViewName, modelDefaultValues, viewData); } /// <summary> /// Tries to reuse old .Index values from the HttpRequest in order to keep the ModelState consistent /// across requests. If none are left returns a new one. /// </summary> /// <param name="collectionIndexFieldName"></param> /// <returns>a GUID string</returns> private static string GetCollectionItemIndex(string collectionIndexFieldName) { Queue<string> previousIndices = (Queue<string>)HttpContext.Current.Items[collectionIndexFieldName]; if (previousIndices == null) { HttpContext.Current.Items[collectionIndexFieldName] = previousIndices = new Queue<string>(); string previousIndicesValues = HttpContext.Current.Request[collectionIndexFieldName]; if (!String.IsNullOrWhiteSpace(previousIndicesValues)) { foreach (string index in previousIndicesValues.Split(',')) previousIndices.Enqueue(index); } } return previousIndices.Count > 0 ? previousIndices.Dequeue() : Guid.NewGuid().ToString(); } private class CollectionItemNamePrefixScope : IDisposable { private readonly TemplateInfo _templateInfo; private readonly string _previousPrefix; public CollectionItemNamePrefixScope(TemplateInfo templateInfo, string collectionItemName) { this._templateInfo = templateInfo; _previousPrefix = templateInfo.HtmlFieldPrefix; templateInfo.HtmlFieldPrefix = collectionItemName; } public void Dispose() { _templateInfo.HtmlFieldPrefix = _previousPrefix; } } }
这样就能很方便的解决ASP.NET MVC提交包含集合类型且数量不定的数据时存在的问题。
希望这边博客对大家使用ASP.NET MVC编写web项目有所帮助,欢迎大家一起讨论和指正。
相关文章推荐
- 如何实现IEnumerable<T>和IEnumerator接口,一个ASP.NET MVC日志模型的实现
- ASP.NET MVC前端提交List<Model>到controller
- 在ASP.NET MVC 模型中 选择最好的方法将多个model(数据模型)传递到视图
- ASP.NET MVC(模型(Model),视图(View)和控制Controller)实践:实现身份验证权限管理
- 如何使用jQuery向asp.net Mvc传递复杂json数据-ModelBinder篇
- ASP.NET MVC:如何实现一个自己的ModelBinder?
- 如何应用Asp.Net Mvc内建功能(DefaultModelBinder)实现简单类型、复杂类型、集合类型,以及字典类型的自动绑定
- Asp.net MVC中提交集合对象,实现Model绑定
- ASP.NET MVC中 Jquery AJAX 获取数据利用MVC模型绑定实现输出
- Asp.net MVC中提交集合对象,实现Model绑定
- Asp.net MVC中提交集合对象,实现Model绑定
- ASP.NET MVC中 Jquery AJAX 获取数据利用MVC模型绑定实现输出
- ASP.NET MVC AJAX实现 以及如何处理json数据,简介
- ASP.NET MVC:如何实现一个自己的ModelBinder?
- ASP.NET MVC 基于强类型视图下 对模型以及模型中List 字段的同时编辑 表单提交
- Asp.net MVC中提交集合对象,实现Model绑定(转载)
- ASP.NET MVC中使用View Model分离领域模型
- ASP.NET MVC 入门7、Hellper与数据的提交与绑定
- 关于ASP.net MVC 中DropDownList绑定与提交数据
- ASP.NET MVC中实现多个按钮提交的几种方法