使用ASP添加相关实体.NET MVC和Razor



我有一个Person类,它具有导航属性ICollection<Address> AddressesAddress实体具有属性City

我想给用户一个创建/更新Person及其地址的选项,包括添加和删除地址。

因此,为了显示地址,我只需调用@Html.EditorFor(m => m.Addresses),剃刀引擎使用我的自定义模板来处理集合,该模板位于EditorTemplates文件夹中,为每个字段生成类似id="Addresses_0__City" name="Addresses[0].City"的匹配签名
到目前为止还不错。

问题是添加新的Address es。
我创建了一个按钮,当单击该按钮时,jQuery函数会调用一个操作(通过自定义EditorTemplate呈现),但其字段没有如上所述的签名,只有id="City" name="City",因此在后操作中不会被识别为Person实体的一部分。

如何使用正确的签名生成idname字段?

我已经阅读了这篇文章和其他许多文章,但没有发现任何一篇涉及idname的问题。

根据评论中的建议,我最终使用了以下内容。

无论如何,我不得不将新项包装为集合,并且隐藏字段只是附加在集合项之后,而不是注入(因为在移除时它会保留在那里),这让我很困扰。

因此,我最终添加了以下扩展名,用于Razorcshtml文件,以及在向集合添加新项目时调用的操作:

以下是扩展(还有一些过载,请参阅此处的完整代码):

private static string EditorForManyInternal<TModel, TValue>(HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> expression, IEnumerable<TValue> collection, string templateName)
{
  var sb = new StringBuilder();
  var prefix = html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;
  var htmlFieldName = (prefix.Length > 0 ? (prefix + ".") : String.Empty) + ExpressionHelper.GetExpressionText(expression);
  var items = collection ?? expression.Compile()(html.ViewData.Model);
  foreach (var item in items)
  {
    var guid = Guid.NewGuid().ToString();
    var dummy = new { Item = item };
    var memberExp = Expression.MakeMemberAccess(Expression.Constant(dummy), dummy.GetType().GetProperty("Item"));
    var singleItemExp = Expression.Lambda<Func<TModel, TValue>>(memberExp, expression.Parameters);
    var editor = html.EditorFor(singleItemExp, templateName, string.Format("{0}[{1}]", htmlFieldName, guid));
    var hidden = String.Format(@"<input type='hidden' name='{0}.Index' value='{1}' />", htmlFieldName, guid);
    var eNode = HtmlNode.CreateNode(editor.ToHtmlString().Trim());
    if (eNode is HtmlTextNode)
      throw new InvalidOperationException("Unsuported element.");
    if (eNode.GetAttributeValue("id", "") == "")
      eNode.SetAttributeValue("id", guid);
    var hNode = HtmlNode.CreateNode(hidden);
    eNode.AppendChild(hNode);
    sb.Append(eNode.OuterHtml);
  }
  return sb.ToString();
}
public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> expression, string templateName)
{
  var value = EditorForManyInternal(html, expression, null, templateName);
  return new MvcHtmlString(value);
}

视图中的用途:

<div>
  <h4>@Resources.Person.Children</h4>
  <ul id="patientChildren" class="list-group ajax-collection">
    @Html.EditorForMany(m => m.Children)
  </ul>
  @Ajax.ActionLink("Create Child", "CreateChild", new { patientId = Model.Id, lastName = Model.LastName }, new AjaxOptions { UpdateTargetId = "patientChildren", InsertionMode = InsertionMode.InsertAfter, OnSuccess = CommonData.AjaxOnSuccessJsFuncName }, new { @class = "button btn-default" })
</div>

以下是被调用的ajax函数(重要的是要将生成的项目用ajax-collection-item进行分类,并将移除按钮用btn remove进行分类):

//#region Ajax add and remove
var ajaxCollectionItemSelector = '.ajax-collection-item';
function attachAjaxRemoveHandlers(id) {
  var context = $(id ? '#' + id : ajaxCollectionItemSelector);
  var removeButton = context.find('.btn.remove');
  removeButton.click(function () {
    var button = $(this);
    var collectionItem = button.closest(ajaxCollectionItemSelector);
    collectionItem.remove();
  });
};
function ajaxOnSuccess(ajaxContext) {
  var collectionItem = $(ajaxContext);
  var id = collectionItem.prop('id');
  attachAjaxRemoveHandlers(id);
  //TODO: following line doesn't work
  collectionItem.find(':text:first-of-type').focus();
};
function runCommonScripts() {
  attachAjaxRemoveHandlers();
};
//#endregion Ajax add and remove

新项目操作(CreateChild)如下所示(EditorForSingle扩展位于同一位置:

public ContentResult CreateChild(int patientId, string lastName)
{
  return this.EditorForSingle((Patient p) => p.Children, 
    new PatientChild
    { 
      PatientId = patientId, 
      LastName = lastName 
    });
}

最新更新