EditorForMany不适用于深度超过1级的对象

时间:2017-02-08 15:10:42

标签: asp.net ajax asp.net-mvc razor

我有一个示例asp.net mvc5程序,其中我尝试构建一个支付模型,其中添加了许多级别的部分以创建完整的对象。在这个例子中,我使用的是通用数据。我有一个顶级'测试',你可以添加多个' A1'对象,你可以添加多个' B2'对象。

表单使用ajax和jquery来允许此人动态添加数据,然后在按下提交按钮时立即提交数据。

我找到了由html helper 制作的Matt Lunn作为editorForMany。它运作良好,将我的所有信息添加到网页,但它永远不会回发深度超过2级的模型(顶部,附加a1')。

我可以在我的页面上构建整个模型。它看起来很合适,但是当我回发时,A1下面没有任何东西显示出来。我可以根据需要添加尽可能多的A1。如果我更改了代码并直接对测试进行了测试,那么这样就行了,但是没有任何内容可以像我一样添加在

这是我的代码。我为这篇文章的格式和长度道歉。

MVC助手

 public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> propertyExpression, Expression<Func<TValue, string>> indexResolverExpression = null, bool includeIndexField = true) where TModel : class
{
    var items = propertyExpression.Compile()(html.ViewData.Model);
    var htmlBuilder = new StringBuilder();
    var htmlFieldName = ExpressionHelper.GetExpressionText(propertyExpression);
    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, TValue>>(memberExp, propertyExpression.Parameters);

        if (String.IsNullOrEmpty(guid))
        {
            guid = Guid.NewGuid().ToString();
        }
        else
        {
            guid = html.AttributeEncode(guid);
        }

        if (includeIndexField)
        {
            htmlBuilder.Append(_EditorForManyIndexField<TValue>(htmlFieldNameWithPrefix, guid, indexResolverExpression));
        }

        htmlBuilder.Append(html.EditorFor(singleItemExp, null, String.Format("{0}[{1}]", htmlFieldName, guid)));
    }
    MvcHtmlString m1 = new MvcHtmlString(htmlBuilder.ToString());
    return m1;
}

测试控制器

public class testingController : Controller
{
    // GET: testing
    public ActionResult startTest()
    {
        var model = new testing();
        return View(model);
    }

    [HttpPost]
    public ActionResult startTest([Bind] testing model)
    {
        if (ModelState.IsValid)
        {
            var r = 1;
        }

        return View(model);

    }

    [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
    public ActionResult addA1()//current running test 
    {
        var model = new testing();
        model.aas.Add(new A1());
        return View(model);
    }

    [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
    public ActionResult addB2()//current running test 
    {
        var model = new A1();
        model.bbs.Add(new B2());
        return View(model);
    }

}

模型类

public class testing
{
    public string name { get; set; }
    public List<A1> aas { get; set; }
    public testing()
    {
        aas = new List<A1>();
    }
}
public class A1
{
    public string aName { get; set; }
    public List<B2> bbs { get; set; }
    public A1()
    {
        bbs = new List<B2>();
    }
}
public class B2
{
    public string bName { get; set; }
    public B2() { }
}

startTest.cshtml

@model proofOfConceptPaymentBuilder.Models.testing
@{
ViewBag.Title = "startTest";
}
@section Scripts
{
<script>
jQuery(document).ready(function ($) {
$('#add-bbs').on('click', function () {
jQuery.get('/testing/addB2').done(function (html) {
$('#bbsList').append(html);
});
});    
$('#add-aas').on('click', function () {
jQuery.get('/testing/addA1').done(function (html) {
$('#aasList').append(html);
});
});     
});
function alertSomething() {
alert('something');
jQuery.get('/testing/addB2').done(function (html) {
$('#bbsList').append(html);
});
};
</script>
}
@using (Html.BeginForm())
{
<h2>Create</h2>
@Html.EditorFor(x => x)
<input type="submit" />
}

EditorTemplates (测试编辑器模板)

@model proofOfConceptPaymentBuilder.Models.testing
<div class="form-group">
@Html.LabelFor(x => x.name)
@Html.EditorFor(x => x.name)
</div>
<div class="form-group">
<div id="aasList">
    @Html.EditorFor(x => x.aas)
</div>
    <input type="button" id="add-aas" value="add aas" />
    <input type="button" id="delete-testing" value="delete test" />
</div>

(a1编辑模板)

@model proofOfConceptPaymentBuilder.Models.A1
<div class="form-group">
@Html.LabelFor(x => x.aName)
@Html.EditorFor(x => x.aName)
</div>
<div class="form-group">
<div id="bbsList">
    @Html.EditorForMany(x => x.bbs)
</div>
<input type="button" id="add-bbs" value="add bss" onclick="alertSomething()"/>
<input type="button" id="delete-aas" value="delete ass" />
</div>

(b2编辑模板)

@model proofOfConceptPaymentBuilder.Models.B2
<div class="form-group">
@Html.LabelFor(x => x.bName)
@Html.EditorFor(x => x.bName)
</div>
<div>
<input type="button" id="delete-bss" value="delete bbs" />
</div>

addA1.cshtml

@model proofOfConceptPaymentBuilder.Models.testing
@{
     Layout = null;
}
@Html.EditorForMany(x => x.aas)

addB2.cshtml

@model proofOfConceptPaymentBuilder.Models.A1
@{ 
    Layout = null;
}
@Html.EditorForMany(x => x.bbs)

好的,我很抱歉格式化。我对这里的格式有点新意。我必须手动缩进我的代码才能显示在代码块中。

1 个答案:

答案 0 :(得分:0)

我也偶然发现了这个问题,考虑到我在StackOverflow上发现的类似问题,我创建了一个.NET Core库来解决这个问题。它称为DynamicVML (Dynamic View-Model Lists),您可以使用NuGet来获取它。

基本上,它是ASP.NET Core的列表模板引擎,可用于显示任何深度的视图模型列表。

您可以像这样使用它:

@Html.ListEditorFor(x => x.Addresses, 
    Url.Action("AddAddress"), // the action in your controller that creates views
    "Add new address", // some text for the "add new item button" in your form
    listContainerTemplate: "viewThatWrapsTheList", 
    listTemplate: "viewForTheList",
    itemContainerTemplate: "viewThatWrapsYourViewModel",
    ItemTemplate: "viewForYourViewModel") 

现在,您实际上不必指定所有这些内容。如果您愿意,您可以只是

@Html.ListEditorFor(x => x.Addresses, Url.Action("AddAddress"), "Add new address")

它将猜测其余的所有内容。

它是问题的核心,它将支持任何深度的嵌套。