【问题标题】:Edits in List not being saved when partial view loaded MVC部分视图加载 MVC 时未保存列表中的编辑
【发布时间】:2017-04-01 00:33:01
【问题描述】:

我是 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)      
{
    ....
}

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

【问题讨论】:

    标签: ajax asp.net-mvc


    【解决方案1】:

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

    在主视图中,您有一个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
    }
    

    【讨论】:

    • 我使用了解决方案 2,效果很好。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多