【问题标题】:POST a form array without successfulPOST 一个表单数组没有成功
【发布时间】:2015-05-23 13:32:41
【问题描述】:

我正在使用 C# 和 .NET Framework 4.5.1 开发一个 ASP.NET MVC 5 Web。

我在cshtml 文件中有这个form

@model MyProduct.Web.API.Models.ConnectBatchProductViewModel

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Create</title>
</head>
<body>
    @if (@Model != null)
    { 
        <h4>Producto: @Model.Product.ProductCode, Cantidad: @Model.ExternalCodesForThisProduct</h4>
        using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
        {
            @Html.HiddenFor(model => model.Product.Id, new { @id = "productId", @Name = "productId" });

            <div>
                <table id ="batchTable" class="order-list">
                    <thead>
                        <tr>
                            <td>Cantidad</td>
                            <td>Lote</td>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>@Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].Quantity")</td>
                            <td>@Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].BatchName")</td>
                            <td><a class="deleteRow"></a></td>
                        </tr>
                    </tbody>
                    <tfoot>
                        <tr>
                            <td colspan="5" style="text-align: left;">
                                <input type="button" id="addrow" value="Add Row" />
                            </td>
                        </tr>
                    </tfoot>
                </table>
            </div>
            <p><input type="submit" value="Seleccionar" /></p>
        }
    }
    else
    { 
        <div>Error.</div>
    }
<script src="~/Scripts/jquery-2.1.3.min.js"></script>
<script src="~/js/createBatches.js"></script> <!-- Resource jQuery -->    
</body>
</html>

这是动作方法:

[HttpPost]
public ActionResult Save(FormCollection form)
{
    return null;
}

还有两个ViewModel

public class BatchProductViewModel
{
    public int Quantity { get; set; }
    public string BatchName { get; set; }
}

public class ConnectBatchProductViewModel
{
    public Models.Products Product { get; set; }
    public int ExternalCodesForThisProduct { get; set; }

    public IEnumerable<BatchProductViewModel> BatchProducts { get; set; }
}

但我在FormCollection form var 中得到了这个:

但我想得到一个IEnumerable&lt;BatchProductViewModel&gt; model

public ActionResult Save(int productId, IEnumerable<BatchProductViewModel> model);

如果我使用上面的方法签名,两个参数都是空的。

我想要一个IEnumerable,因为用户将使用 jQuery 动态添加更多行。

这是jQuery 脚本:

jQuery(document).ready(function ($) {
    var counter = 0;

    $("#addrow").on("click", function () {

        counter = $('#batchTable tr').length - 2;

        var newRow = $("<tr>");
        var cols = "";

        var quantity = 'ConnectBatchProductViewModel.BatchProducts[0].Quantity'.replace(/\[.{1}\]/, '[' + counter + ']');
        var batchName = 'ConnectBatchProductViewModel.BatchProducts[0].BatchName'.replace(/\[.{1}\]/, '[' + counter + ']');

        cols += '<td><input type="text" name="' + quantity + '"/></td>';
        cols += '<td><input type="text" name="' + batchName + '"/></td>';

        cols += '<td><input type="button" class="ibtnDel"  value="Delete"></td>';
        newRow.append(cols);

        $("table.order-list").append(newRow);
        counter++;
    });


    $("table.order-list").on("click", ".ibtnDel", function (event) {
        $(this).closest("tr").remove();

        counter -= 1
        $('#addrow').attr('disabled', false).prop('value', "Add Row");
    });
});

有什么想法吗?

我已经检查了这个 SO answerthis article,但我的代码无法正常工作。

【问题讨论】:

  • 请同时发布BatchProductViewModel 的代码...另外,在您的操作方法中,您确定您的意图是使用BatchProductViewModel 而不是ConnectBatchProductViewModel
  • @Ruslan 问题已更新。
  • 你在视图中的模型是ConnectBatchProductViewModel 如果你想为BatchProductViewModel的集合生成一个视图,那么你的视图需要是IEnumerable&lt;BatchProductViewModel&gt;并且POST方法参数需要相同(不要使用FormCollection)并且BatchProductViewModel的控件需要在for循环中生成
  • 并且int productId 永远不会被绑定,因为您的控件的名称是Product.Id(不是productId)。如果将方法更改为public ActionResult Save(ConnectBatchProductViewModel model),您将看到它已正确绑定
  • 我已经更新了我的问题。我已经更改了视图模型,并且得到了相同的结果。我想要一个IEnumerable,因为我想使用 jQuery 动态添加更多行。

标签: c# html asp.net-mvc forms


【解决方案1】:

遵循 DRY 的原则,您可以为此创建一个 EditorTemplate。 步骤:

1- 在视图中 > 共享 > 创建名为 (EditorTemplates) 的新文件夹

2- 在新创建的 EditorTemplates 文件夹中创建一个视图,根据 OP 示例,该视图的模型应该是 BatchProductViewModel。将您的代码放在编辑器视图中。不需要循环或索引。

EditorTemplate 的作用类似于每个子实体的 PartialView,但以更通用的方式。

3- 在您的父实体的视图中,调用您的编辑器: @Html.EditorFor(m => m.BatchProducts)

这不仅提供了更有条理的视图,还可以让您在其他视图中重复使用相同的编辑器。

【讨论】:

    【解决方案2】:

    using(Html.BeginForm())
    {
      // code here 
    
    }

    在发布表单数据时,所有标签都必须包含在表单标签中。

    【讨论】:

      【解决方案3】:

      您可以通过video tutorial访问this article获取完整源代码。

      你必须先创建一个动作,我们可以从中传递对象列表

      [HttpGet]
      public ActionResult Index()
      {
          List<Contact> model = new List<Contact>();
          using (MyDatabaseEntities dc = new MyDatabaseEntities())
          {
              model = dc.Contacts.ToList();
          }
          return View(model);
      }
      

      那么我们需要为那个动作创建一个视图

      @model List<UpdateMultiRecord.Contact>
      @{
          ViewBag.Title = "Update multiple row at once Using MVC 4 and EF ";
      }
      @using (@Html.BeginForm("Index","Home", FormMethod.Post))
      {
          <table>
                  <tr>
                      <th></th>               
                      <th>Contact Person</th>
                      <th>Contact No</th>
                      <th>Email ID</th>
                  </tr>
              @for (int i = 0; i < Model.Count; i++)
              {
                  <tr>               
                      <td> @Html.HiddenFor(model => model[i].ContactID)</td>
                      <td>@Html.EditorFor(model => model[i].ContactPerson)</td>
                      <td>@Html.EditorFor(model => model[i].Contactno)</td>
                      <td>@Html.EditorFor(model => model[i].EmailID)</td>
                  </tr>
              }
          </table>
          <p><input type="submit" value="Save" /></p>
          <p style="color:green; font-size:12px;">
              @ViewBag.Message
          </p>
      }
       @section Scripts{
          @Scripts.Render("~/bundles/jqueryval")
       }
      

      然后我们必须编写代码来将对象列表保存到数据库中

      [HttpPost]
      public ActionResult Index(List<Contact> list)
      {  
          if (ModelState.IsValid)
          {
              using (MyDatabaseEntities dc = new MyDatabaseEntities())
              {
                  foreach (var i in list)
                  {
                      var c = dc.Contacts.Where(a =>a.ContactID.Equals(i.ContactID)).FirstOrDefault();
                      if (c != null)
                      {
                          c.ContactPerson = i.ContactPerson;
                          c.Contactno = i.Contactno;
                          c.EmailID = i.EmailID;
                      }
                  }
                  dc.SaveChanges();
              }
              ViewBag.Message = "Successfully Updated.";
              return View(list);
          }
          else
          {
              ViewBag.Message = "Failed ! Please try again.";
              return View(list);
          }
      }
      

      【讨论】:

        【解决方案4】:

        您需要在for 循环中为集合生成控件,以便使用索引器正确命名它们(注意属性BatchProducts 需要为IList&lt;BatchProductViewModel&gt;

        @using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
        {
          ....
          <table>
            ....
            @for(int i = 0; i < Model.BatchProducts.Count; i++)
            {
              <tr>
                <td>@Html.TextBoxFor(m => m.BatchProducts[i].Quantity)</td>
                <td>@Html.TextBoxFor(m => m.BatchProducts[i].BatchName)</td>
                <td>
                  // add the following to allow for dynamically deleting items in the view
                  <input type="hidden" name="BatchProducts.Index" value="@i" />
                  <a class="deleteRow"></a>
                </td>
              </tr>
            }
            ....
          </table>
          ....
        }
        

        那么POST方法需要是

        public ActionResult Save(ConnectBatchProductViewModel model)
        {
          ....
        }
        

        编辑

        注意:除了您的编辑之外,如果您想在他的视图中动态添加和删除BatchProductViewModel 项目,您将需要使用BeginCollectionItem 帮助程序或this answer 中讨论的html 模板

        动态添加新项目的模板是

        <div id="NewBatchProduct" style="display:none">
          <tr>
            <td><input type="text" name="BatchProducts[#].Quantity" value /></td>
            <td><input type="text" name="BatchProducts[#].BatchName" value /></td>
            <td>
              <input type="hidden" name="BatchProducts.Index" value ="%"/>
              <a class="deleteRow"></a>
            </td>
          </tr>
        </div>
        

        注意虚拟索引器和隐藏输入的不匹配值会阻止此模板回发。

        那么添加新BatchProducts 的脚本将是

        $("#addrow").click(function() {
          var index = (new Date()).getTime(); // unique indexer
          var clone = $('#NewBatchProduct').clone(); // clone the BatchProducts item
          // Update the index of the clone
          clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
          clone.html($(clone).html().replace(/"%"/g, '"' + index  + '"'));
          $("table.order-list").append(clone.html());
        });
        

        【讨论】:

        • 我想要一个IEnumerable,因为用户将使用 jQuery 动态添加更多行。
        • 查看一些技术的更新来做到这一点(它不需要IEnumerable&lt;T&gt; - 它可以很容易地成为IList&lt;T&gt;
        • 请注意,您的模型没有名为 ConnectBatchProductViewModel 的属性,因此 ConnectBatchProductViewModel.BatchProducts[#].Quantity 不会绑定到任何东西。必须是 BatchProducts[#].Quantity 才能绑定。
        • 接下来,你不会为每个项目生成一个&lt;input name="BatchProducts.Index" value="#" /&gt;控件,所以一旦你删除一个项目,绑定就会失败。
        • 不添加 BeginCollectionItem 并使用 &lt;td&gt;@Html.TextBox("BatchProducts[0].Quantity")&lt;/td&gt; 它可以工作。我在ConnectBatchProductViewModel model 参数中正确获取了所有数据。但是如果我有三行并且用户删除了第二行,我不会得到model参数中的所有数据。
        【解决方案5】:

        在您的 Post Methode 中,您会收到“MyProduct.Web.API.Models.ConnectBatchProductViewModel”作为参数。
        使用 Post 方法的现有模型。

        为什么要从模型中获取 IEnumerable?只有一个可用,包括模型中的 id。

        【讨论】:

        • 我想要一个IEnumerable,因为我想使用 jQuery 动态添加更多行。
        • 然后将模型更改为 IEnumerable
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-04
        • 2014-08-16
        • 2013-03-22
        相关资源
        最近更新 更多