部分视图加载MVC时,列表中的编辑未被保存

时间:2017-04-01 00:33:02

标签: ajax asp.net-mvc

我是MVC的新手。我有一个视图,显示附加到QuoteQuoteDetails)的产品。我还有一个Ajax.ActionLink)_用于“添加产品”,它会为要输入的其他产品加载部分视图。问题是加载局部视图时,不会保存对不在局部视图中的其他产品的编辑。如果未加载部分视图,则会保存对列出的产品的编辑。

以下是主视图的相关代码:

@model CMSUsersAndRoles.Models.QuoteViewModel
....
@Scripts.Render("~/Scripts/jquery.unobtrusive-ajax.min.js")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="~/Scripts/jquery.mask.min.js"></script>
@using (Html.BeginForm())
{
    ....
    @Html.HiddenFor(model => model.CustomerId)

    @Html.LabelFor(model => model.QuoteId)
    @Html.EditorFor(model => model.QuoteId, new { htmlAttributes = new { @readonly = "readonly", @class = "form-control" } })
    @Html.ValidationMessageFor(model => model.QuoteId)

    .... // more controls for properties of Quote

    @Html.LabelFor(model => model.QuoteDetail)
    <div id="QuoteDetails">
        @for (int i = 0; i < Model.QuoteDetail.Count; i++)
        {
            @Html.HiddenFor(model => model.QuoteDetail[i].QuoteId, new { htmlAttributes = new { @class = "form-control" } })
            ....     
            @Html.EditorFor(model => model.QuoteDetail[i].SKU, new { htmlAttributes = new { @readonly = "readonly", @id = "SKU", @class = "form-control", style = "width: 100px" } })
            @Html.EditorFor(model => model.QuoteDetail[i].Amount, new { htmlAttributes = new { @class = "form-control amount", style = "width: 95px" } })
            @Html.ValidationMessageFor(model => model.QuoteDetail[i].Amount)

            .... // more for controls for properties of QuoteDetail

            @Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteDetail[i].QuoteId, quoteDetailId = (Model.QuoteDetail[i].QuoteDetailId) },
                new AjaxOptions
                {
                    HttpMethod = "POST",
                    Confirm = "Are you Sure You Want to Delete " + Model.QuoteDetail[i].ProductName,
                }, new { @class = "btn btn-danger glyphicon glyphicon-trash" })
                    </div>
            }
           @Html.EditorFor(model => model.Subtotal, new { htmlAttributes = new { @class = "form-control subTotal", style = "width: 100px; float:right; clear:left; text-align:right" } })

           @Ajax.ActionLink("Add product", "AddProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetail.Count + 1) }, 
               new AjaxOptions
               {
                   UpdateTargetId = "QuoteDetails",
                   InsertionMode = InsertionMode.InsertAfter
               })
            }

以下是部分视图:

@model CMSUsersAndRoles.Models.QuoteDetail

@{
    ViewBag.Title = "EditQuoteDetail";
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
</head>
<body>
    <div id="row" class="row">
        <table>
            @using (Html.BeginCollectionItem("quoteDetail"))
            {
                <tr>
                    @Html.HiddenFor(model => model.QuoteId, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.EditorFor(model => model.SKU, new { htmlAttributes = new { @readonly = "readonly", @id = "SKU", @class = "form-control", style = "width: 100px" } })
                    @Html.DropDownListFor(model => model.ProductId, new SelectList(ViewBag.ProductData, "ProductId", "Name"), "---Select one---", new { style = "width: 300px !important", htmlAttributes = new { @id = "ProductName", @class = "ProductList" } });

                    .... // more controls for properties of QuoteDetail

                    @Ajax.ActionLink(" ", "DeleteProduct", "QuoteViewModel", new { quoteId = Model.QuoteId, quoteDetailId = (Model.QuoteDetailId) },
                          new AjaxOptions
                          {
                              HttpMethod = "POST",
                              Confirm = "Are you Sure You Want to Delete " + Model.ProductName,
                          }, new { @class = "btn btn-danger glyphicon glyphicon-trash" })
                </tr>
            }
        </table>
    </div>

这是控制器动作:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(QuoteViewModel qvm, 
    [Bind(Include = "CustomerId,SalesRep,FirstName,LastName,Company,Address1,Address2,City,State,PostalCode,WorkPhone,CellPhone,Email,Discount,PaymentTerms")] Customer customer,
    [Bind(Include = "QuoteId,QuoteDetailId,ProductId,ProductName,Amount,ListPrice,Discount,Price")] List<QuoteDetail> quoteDetails,
    [Bind(Include = "QuoteId,CustomerId,Subtotal,Tax,Total,QuoteDate,GoodUntil,QuoteSent,DateApproved,DateOrdered")] Quote quote)      
{
    ....
}

任何人都可以帮忙吗?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

您在此处使用了两种不同的技术来生成导致问题的集合。

在主视图中,您有一个for循环来为现有项生成控件,这些控件生成从零开始的连续索引器,这是DefaultModelBinder默认使用的。您的html将包含name属性,例如

<input name="QuoteDetail[0].QuoteId"..../>
<input name="QuoteDetail[1].QuoteId"..../>
<input name="QuoteDetail[2].QuoteId"..../>

但随后您使用BeginCollectionItem辅助方法添加新项目,该方法会将收集索引器生成为Guid,以便新输入(xxxGuid} )

<input name="QuoteDetail[xxxx].QuoteId"..../>

还包括

<input name="QuoteDetail.Index" value="xxxx" ... />

DefaultModelBinder用于匹配非零非连续索引器。你不能同时使用这两种技术。

要解决此问题,您可以在for循环

中为索引器添加输入
@for (int i = 0; i < Model.QuoteDetail.Count; i++)
{
    ....
    <input type="hidden" name="QuoteDetail.Index" value="@i" />
}

或更改循环以使用每次迭代中包含BeginCollectionItem方法的部分视图

@foreach(var item in Model.QuoteDetail)
{
    @Html.Partial("xxxx", item) // replace xxxx with the name of your partial
}