c# – 每次使用新的guid索引时,如何在集合属性上获取验证消息?
在这个示例ASP.Net MVC 4程序中,我有一个用户填写有关赛马的详细信息.比赛有一个名字,以及所涉及的马匹列表.每匹马都有一个名字和一个年龄.
表单使用ajax和javascript来允许此人动态添加和删除马输入字段,然后在按下提交按钮时立即提交. 为了让这个过程变得简单,我使用Matt Lunn制造的html helper. public static MvcHtmlString EditorForMany<TModel,TValue>(this HtmlHelper<TModel> html,Expression<Func<TModel,IEnumerable<TValue>>> expression,string htmlFieldName = null) where TModel : class { var items = expression.Compile()(html.ViewData.Model); var sb = new StringBuilder(); if (String.IsNullOrEmpty(htmlFieldName)) { var prefix = html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix; htmlFieldName = (prefix.Length > 0 ? (prefix + ".") : String.Empty) + ExpressionHelper.GetExpressionText(expression); } foreach (var item in items) { var dummy = new { Item = item }; var guid = Guid.NewGuid().ToString(); var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy),dummy.GetType().GetProperty("Item")); var singleItemExp = Expression.Lambda<Func<TModel,TValue>>(memberExp,expression.Parameters); sb.Append(String.Format(@"<input type=""hidden"" name=""{0}.Index"" value=""{1}"" />",htmlFieldName,guid)); sb.Append(html.EditorFor(singleItemExp,null,String.Format("{0}[{1}]",guid))); } return new MvcHtmlString(sb.ToString()); } 虽然我不了解所有细节(请阅读博客文章),但我知道它将索引值更改为guids而不是顺序整数.这允许我删除列表中间的项目,而无需重新计算索引. 这是我的MCVE的其余代码 HomeController.cs public class HomeController : Controller { [HttpGet] public ActionResult Index() { var model = new Race(); //start with one already filled in model.HorsesInRace.Add(new Horse() { Name = "Scooby",Age = 10 }); return View(model); } [HttpPost] public ActionResult Index(Race postedModel) { if (ModelState.IsValid) //model is valid,redirect to another page return RedirectToAction("ViewHorseListing"); else //model is not valid,show the page again with validation errors return View(postedModel); } [HttpGet] public ActionResult AjaxMakeHorseEntry() { //new blank horse for ajax call var model = new List<Horse>() { new Horse() }; return PartialView(model); } } Models.cs public class Race { public Race() { HorsesInRace = new List<Horse>(); } [Display(Name = "Race Name"),Required] public string RaceName { get; set; } [Display(Name = "Horses In Race")] public List<Horse> HorsesInRace { get; set; } } public class Horse { [Display(Name = "Horse's Name"),Required] public string Name { get; set; } [Display(Name = "Horse's Age"),Required] public int Age { get; set; } } Index.cshtml @model CollectionAjaxPosting.Models.Race <h1>Race Details</h1> @using (Html.BeginForm()) { @Html.ValidationSummary() <hr /> <div> @Html.DisplayNameFor(x => x.RaceName) @Html.EditorFor(x => x.RaceName) @Html.ValidationMessageFor(x => x.RaceName) </div> <hr /> <div id="horse-listing">@Html.EditorForMany(x => x.HorsesInRace)</div> <button id="btn-add-horse" type="button">Add New Horse</button> <input type="submit" value="Enter Horses" /> } <script type="text/javascript"> $(document).ready(function () { //add button logic $('#btn-add-horse').click(function () { $.ajax({ url: '@Url.Action("AjaxMakeHorseEntry")',cache: false,method: 'GET',success: function (html) { $('#horse-listing').append(html); } }) }); //delete-horse buttons $('#horse-listing').on('click','button.delete-horse',function () { var horseEntryToRemove = $(this).closest('div.horse'); horseEntryToRemove.prev('input[type=hidden]').remove(); horseEntryToRemove.remove(); }); }); </script> 查看/共享/ EditorTemplates / Horse.cshtml @model CollectionAjaxPosting.Models.Horse <div class="horse"> <div> @Html.DisplayNameFor(x => x.Name) @Html.EditorFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name) </div> <div> @Html.DisplayNameFor(x => x.Age) @Html.EditorFor(x => x.Age) @Html.ValidationMessageFor(x => x.Age) </div> <button type="button" class="delete-horse">Remove Horse</button> <hr /> </div> 查看/主页/ AjaxMakeHorseEntry.cshtml @model IEnumerable<CollectionAjaxPosting.Models.Horse> @Html.EditorForMany(x => x,"HorsesInRace") 数据流与此代码一起使用.一个人能够在页面上尽可能多地创建和删除马条目,并且在提交表单时,所有输入的值都被赋予操作方法. 但是,如果用户未输入马条目的[必需]信息,则ModelState.IsValid将显示为false,同时显示该表单,但不会显示Horse属性的验证消息.验证错误确实显示在ValidationSummary列表中. 例如,如果将“种族名称”留空,并且还有一个“马”的名称,则会显示前者的验证消息.后者将进行验证< span>使用“field-validation-valid”类. 我非常确定这是因为EditorForMany方法在每次创建页面时为每个属性创建新的guid,因此验证消息无法与正确的字段匹配. 我该怎么做才能解决这个问题?我是否需要放弃guid索引创建,还是可以对EditorForMany方法进行更改以允许验证消息正确传递? 解决方法
是的;这正是这里发生的事情. 要解决这个问题,我们需要修改EditorForMany(),以便它重新使用项目的GUID,而不是生成新项目.反过来,这意味着我们需要跟踪GUID分配给哪个项目,以便可以重复使用. 前者可以通过对EditorForMany()的内部修改来完成.后者要求我们: >将属性添加到我们的模型中,可以在其中存储指定的GUID 这使得EditorForMany帮助器看起来像这样; public static class HtmlHelperExtensions { public static MvcHtmlString EditorForMany<TModel,IEnumerable<TValue>>> propertyExpression,Expression<Func<TValue,string>> indexResolverExpression = null,string htmlFieldName = null) where TModel : class { htmlFieldName = htmlFieldName ?? ExpressionHelper.GetExpressionText(propertyExpression); var items = propertyExpression.Compile()(html.ViewData.Model); var htmlBuilder = new StringBuilder(); var htmlFieldNameWithPrefix = html.ViewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName); Func<TValue,string> indexResolver = null; if (indexResolverExpression == null) { indexResolver = x => null; } else { indexResolver = indexResolverExpression.Compile(); } foreach (var item in items) { var dummy = new { Item = item }; var guid = indexResolver(item); var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy),dummy.GetType().GetProperty("Item")); var singleItemExp = Expression.Lambda<Func<TModel,propertyExpression.Parameters); if (String.IsNullOrEmpty(guid)) { guid = Guid.NewGuid().ToString(); } else { guid = html.AttributeEncode(guid); } htmlBuilder.Append(String.Format(@"<input type=""hidden"" name=""{0}.Index"" value=""{1}"" />",htmlFieldNameWithPrefix,guid)); if (indexResolverExpression != null) { htmlBuilder.Append(String.Format(@"<input type=""hidden"" name=""{0}[{1}].{2}"" value=""{1}"" />",guid,ExpressionHelper.GetExpressionText(indexResolverExpression))); } htmlBuilder.Append(html.EditorFor(singleItemExp,guid))); } return new MvcHtmlString(htmlBuilder.ToString()); } } 然后我们还需要更改模型,添加存储GUID的属性; public class Race { public Race() { HorsesInRace = new List<Horse>(); } [Display(Name = "Race Name"),Required] public int Age { get; set; } // Note the addition of Index here. public string Index { get; set; } } …最后,改变我们对EditorForMany()的使用以使用新签名; Index.cshtml; <div id="horse-listing">@Html.EditorForMany(x => x.HorsesInRace,x => x.Index)</div> AjaxMakeHorseEntry.cshtml; @Html.EditorForMany(x => x,x => x.Index,"HorsesInRace") …然后应该出现验证消息. 顺便说一句,我建议不要为EditorForMany使用htmlFieldName参数,而是将控制器操作更改为; [HttpGet] public ActionResult AjaxMakeHorseEntry() { var model = new Race(); model.HorsesInRace.Add(new Horse()); return PartialView(model); } …然后你的AjaxMakeHorseEntry.cshtml视图就是这样; @model Models.Race @Html.EditorForMany(x => x.HorsesInRace,x => x.Index) 否则,嵌套使用EditorForMany()时生成的名称属性会中断. 我将更新博客文章以使用上述版本的EditorForMany(),而不是接受htmlFieldName参数,因此. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |