【问题标题】:MVC Ajax Form inside a For LoopFor 循环内的 MVC Ajax 表单
【发布时间】:2015-01-23 17:29:55
【问题描述】:

我在 For 循环中使用 Ajax.BeginForm 允许我为 for 循环中的每个项目发布到控制器。最初,for 循环在页面加载时正确呈现每个项目。 当控制器处理了从 Ajax.BeginForm 正确填充的传入视图模型时,该方法将新生成的视图模型发回给视图,但是我的 For 循环中的每个项目现在都是重复的,并且所有文本框现在都有提交的视图模型的值。

非常困惑如何正确构建此代码,我需要提交基于 ajax 和部分视图的工作。我确实看过使用 JQuery 提交,但担心我可能会丢失 AntiForgeryToken 并且 Ajax.BeginForm 是一种更优雅的方法。非常感谢任何帮助。也很高兴提供更多信息。

查看

@model Namespace.Models.MyParentViewModel
@for (int i = 0; i < Model.Items.Count; i++)
{
    using (Ajax.BeginForm("SaveItem", "Controller", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "pensions" }))
    {
      @Html.AntiForgeryToken()
      @Html.ValidationSummary()
      <div>
         @Html.Hidden("ID", Model.Items[i].ID)
         @Html.TextBox("TheName", Model.Items[i].TheName, new { @class = "form-control", @id = "item-" + i})
         <input type="submit" value="Save" class="btn save" name="Command" />
      </div>
    }
}

控制器

[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("SaveItem")]
public ActionResult SaveItem(MyItemViewModel mivm, string Command)
{
   if (ModelState.IsValid)
   {
       #do some logic
   }

   // Return a newly populated MyViewModel with updated mivm.
   var model = PopulateMyParentViewModel();
   return PartialView("_MyPartialView", model);
}

private MyParentViewModel PopulateMyParentViewModel()
{
   List<MyItemsViewModel> lstItems = new List<MyItemsViewModel>();
   foreach (var item in enity.items.OrderBy(p => p.ID).ToList())
   {
     var ExistingItem = new MyItemViewModel
     {
        ID = item.ID,
         TheName = item.TheName
     };

     lstItems.Add(MyItemViewModel);
   }
   MyParentViewModel.Items = lstItems;
   return MyParentViewModel
}

【问题讨论】:

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


    【解决方案1】:

    我不确定您的代码中的“养老金”是什么,我猜它是围绕循环生成的每个表单的 div?如果是这样,您的问题如下:

      1234563 - 考虑只发回受影响“记录”的更新显示,并用响应更新相应的项目。
    • 导致项目重复的原因:AjaxOptions 类有一个属性InsertionMode,我记得,它的默认值是InsertionMode = InsertionMode.InsertAfter,这会导致出现重复项。为什么?您向服务器发送一个 Ajax 请求,服务器发送回一个由整个列表填充的 HTML 片段,而不仅仅是一个项目,然后浏览器将该片段附加到现有记录中。

    解决方案

    • 如果您重新制作项目以仅发回一条记录而不是所有记录,则只需在 using(...) 块周围添加唯一标识(= 具有唯一 id 属性)&lt;div&gt; 元素,设置 @987654327 @ 到 InsertionMode=InsertionMode.Replace 并将 UpdateTargetId 属性设置为 id&lt;div&gt;,如下所示:

      <div id="record-@i">    
      @using (Ajax.BeginForm("SaveItem", "Controller", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "record-" + i.ToString(), InsertionMode = InsertionMode.Replace }))
      {
          // ...
      }
      </div>
      

      这将导致 Ajax 响应替换发送请求的容器的 contents(通过 contents 我的意思是包装元素本身没有被替换!对于该行为,您需要使用 InsertionMode.ReplaceWith)。如果你弄错了,你最终会在宿主容器中倾倒大量的嵌套元素,可能会破坏使用非常特定选择器的脚本和/或样式。

    • 1234563 987654338@ 属性到id&lt;div&gt;

    --------- 更新------------

    回答您的评论:

    假设您有一个视图 - 为简单起见 - 以表格格式显示记录。然后,结果如下所示:

    Name   | Age | Salary | Hide
    ------------------------------------
    Peter  |  32 |   $15k | Hide button
    Eva    |  28 |   $12k | Hide button
    

    假设您对 Ajax 请求所做的更改是,每当您隐藏一条记录时,您都会发回一个带有按钮的单个表格行,该按钮现在显示“显示”而不是“隐藏”,以及关于所属人员的相同数据触发请求的按钮。 然后,当您发出直接(=非 ajax,当您直接导航到页面时)请求时,您可以这样做:

    @model PeopleCollection
    ...
    <table>
    <thead>
      <tr>
        <td>Name</td>
        <td>Age</td>
        <td>Salary</td>
        <td>Hide</td>
      </tr>
    </thead>
    <tbody>
      @foreach (Person record in Model) {
        @Html.Partial("TableRow_Partial",record)
      }
    </tbody>
    

    “TableRow_Partial”视图如下所示:

    @model Person
    @{
      AjaxOptions opts = new AjaxOptions() {
        UpdateTargetId = "person-row-" + Model.Id.ToString(),
        InsertionMode = InsertionMode.ReplaceWith
        // etc
      };
    }
    ...
    <tr id="person-row-@Model.Id">
      <td>@Model.Name</td>
      <td>@Model.Age</td>
      <td>@Model.Salary</td>
      <td>
        <!-- configure the BeginForm(..) call with the action name and a routeValue anonymous object as "new { id = Model.Id}" -->
        @using (Ajax.BeginForm(...)) {
          <input type="submit" value="@(Model.IsHidden ? "Show" : "Hide")" />
        }
      </td>
    </tr>
    

    这将简单地将循环的构造提取到单独的局部视图中。您的 Ajax 请求的操作方法处理将简单地返回此视图,其中填充了您已更新的一条记录,如下所示:

    public ActionResult UpdateRecord(Int64 id)
    {
      var _record = Repository.Get(id);
      // logic to hide and update record
      return PartialView("TableRow_Partial",_record);
    }
    

    为此,您必须将InsertionMode 设置为InsertionMode.ReplaceWith。所有这一切都是,当您发送 ajax 请求以仅使用一条记录执行某项操作时,您只需刷新一项 - 触发 ajax 调用的一项,然后您只需返回新行(一行!)并替换旧一个和这个新的。

    如果您真的想在每次处理记录时都将整个项目集合发送回客户端,只需将整个呈现的表发送给客户端,并以类似的方式将旧表替换为新表方式。

    【讨论】:

    • 谢谢,但对返回模型有点困惑。如果我使用您解决方案中的代码,我可以将单个模型传递给控制器​​并在数据库中更新该模型,但是当控制器返回项目列表时,该列表将替换记录中的单行-@i 将整个 List 用于 For 循环迭代,仍然会导致重复。 “pensions”实际上是包含这个 for 循环在里面的整个局部视图的 div。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多