VansFannel VansFannel - 1 year ago 60
HTML Question

POST a form array without successful

I'm developing an ASP.NET MVC 5 web with C# and .NET Framework 4.5.1.

I have this

in a

@model MyProduct.Web.API.Models.ConnectBatchProductViewModel

Layout = null;

<!DOCTYPE html>

<meta name="viewport" content="width=device-width" />
@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" });

<table id ="batchTable" class="order-list">
<td><a class="deleteRow"></a></td>
<td colspan="5" style="text-align: left;">
<input type="button" id="addrow" value="Add Row" />
<p><input type="submit" value="Seleccionar" /></p>
<script src="~/Scripts/jquery-2.1.3.min.js"></script>
<script src="~/js/createBatches.js"></script> <!-- Resource jQuery -->

And this is the action method:

public ActionResult Save(FormCollection form)
return null;

And the two

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; }

But I get this in
FormCollection form
enter image description here

But I want to get an
IEnumerable<BatchProductViewModel> model

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

If I use the above method signature both parameters are null.

I want an
because user is going to add more rows dynamically using jQuery.

This is

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>';


$("table.order-list").on("click", ".ibtnDel", function (event) {

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

Any idea?

I have checked this SO answer, and this article but I don't get my code working.

Answer Source

You need to generate the controls for the collection in a for loop so they are correctly named with indexers (note that property BatchProducts needs to be IList<BatchProductViewModel>

@using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
    @for(int i = 0; i < Model.BatchProducts.Count; i++)
        <td>@Html.TextBoxFor(m => m.BatchProducts[i].Quantity)</td>
        <td>@Html.TextBoxFor(m => m.BatchProducts[i].BatchName)</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>

Then the POST method needs to be

public ActionResult Save(ConnectBatchProductViewModel model)


Note: Further to your edit, if you want to dynamically add and remove BatchProductViewModel items in he view, you will need to use the BeginCollectionItem helper or a html template as discussed in this answer

The template to dynamically add new items would be

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

Note the dummy indexers and the non-matching value for the hidden input prevents this template posting back.

Then the script to add a new BatchProducts would be

$("#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  + '"'));