title: ASP.NET MVC Passing a BeginCollectionItem List's Object through AJAX with Javascript Serialize to Controller is passing null
body: <p>This has been giving me some trouble for the past few days.  I'm going to try to be as detailed as possible with what I want and the issue I'm having.  I tried to simplify my controllers, models, etc as much as I can for the sake of the question.</p>
  
  <p><strong>Here's the flow of sequences I'm trying to get to work.</strong></p>
  
  <ol>
  <li>Give user a view with inputs for <code>List&lt;MyObject&gt;</code> in the main ViewModel</li>
  <li>User can hit a button to add another row to add another object to that List of objects to post later</li>
  <li>Hitting the button POSTs the object (in same container) through AJAX from the currently displayed list in order to repeat most of the inputs for the new row (<code>MyObject</code>)</li>
  <li>Controller takes passed <code>MyObject</code> and maps some of the prior values to the new object</li>
  <li>Controller passes back new <code>MyObject</code> and ajax appends the result <code>html</code> to my table</li>
  </ol>
  
  <h1>The Problem</h1>
  
  <p>The Ajax is passing the on-page <code>MyObject</code> as <code>null</code> (or C# doesn't know how to interpret) to my controller (step 4 above).  I can't seem to get the object passed to the controller through the Ajax call.</p>
  
  <h1>What I've got</h1>
  
  <p>Models:</p>
  
  <pre><code>public class MyViewModel
  {
      // irrelevant properties
      public List&lt;MyObject&gt; MyObjects { get; set; } = new List&lt;MyObject&gt;();
  }
  
  public class MyObject
  {
      public int Id { get; set; }
      public string Name { get; set; }
      public string AnotherProperty { get; set; }
      public decimal AdditionalProperty { get; set; }
      public List&lt;MySubObject&gt; MySubObjects { get; set; } = new List&lt;MySubObject&gt;();
  }
  
  public class MySubObject
  {
      public bool IsChecked { get; set; }
      public bool Name { get; set; }
  }
  </code></pre>
  
  <p>Controller GET:</p>
  
  <pre><code>public ActionResult Edit(int? id)
  {
      var viewModel = new MyViewModel();
  
      // for list of checkboxes
      var subObjects = new List&lt;MySubObject&gt;();
  
      // get subobjects from db
      subObjects.Add(new MySubObject {
          Name = "SubObj1", IsChecked = false
      });
      subObjects.Add(new MySubObject {
          Name = "SubObj12",
          IsChecked = false
      });
  
      // populate viewModel with a blank MyObject
      viewModel.MyObjects.Add(new MyObject
      {
          MySubObjects = subObjects
      });
  
      return (viewModel);
  }
  </code></pre>
  
  <p>View (and AJAX call):</p>
  
  <pre><code>&lt;table id="myObjectInputs"&gt;
      @foreach (var obj in Model.MyObjects)
      {
          @Html.Partial("~/Areas/Admin/Views/Shared/EditorTemplates/MyObject.cshtml", obj)
      }
  &lt;/table&gt;
  
  &lt;script&gt;
  $(document).ready(function () {
      $("#myObjectInputs").on("focus", ".make-new-row", function () {
          var plusButton = $(this);
          var currentArea = plusButton.closest(".myobject-container");
  
          var serializedData = currentArea.find(":input").serialize();
  
          // add new row
          $.ajax({
              url: this.href,
              cache: false,
              type: "POST",
              data: { "myObject": serializedData },
              success: function (html) { $("#myObjectInputs").append(html); }
          });
  
          return false;
      });
  });
  &lt;/script&gt;
  </code></pre>
  
  <p>EditorTemplates/MyObject.cshtml:</p>
  
  <pre><code>@using HtmlHelpers.BeginCollectionItem
  @model LD.Areas.Admin.Models.MyObject
  
  &lt;tbody class="myobject-container" style="border:solid 1px red;"&gt;
      @using (Html.BeginCollectionItem("MyObjects"))
      {
          &lt;tr&gt;
              &lt;td&gt;@Html.HiddenFor(x =&gt; x.Id)&lt;/td&gt;
              &lt;td&gt;@Html.EditorFor(x =&gt; x.Name)&lt;/td&gt;
              &lt;td&gt;@Html.EditorFor(x =&gt; x.AnotherProperty)&lt;/td&gt;
              &lt;td&gt;@Html.EditorFor(x =&gt; x.AdditionalProperty)&lt;/td&gt;
              &lt;td&gt;
                  @foreach (var subObj in Model.MySubObjects)
                  {
                      @Html.Partial("~/Areas/Admin/Views/Shared/EditorTemplates/MySubObject.cshtml", subObj)                  
                  }
              &lt;/td&gt;
              &lt;td&gt;
                  &lt;!-- the button that calls the function / AJAX --&gt;
                  @Html.ActionLink("+", "NewMyObjectSection", null, new { @class = "make-new-row" })
              &lt;/td&gt;
          &lt;/tr&gt;
      }
  &lt;/tbody&gt;
  </code></pre>
  
  <p>EditorTemplates/MySubObject.cshtml</p>
  
  <pre><code>@using HtmlHelpers.BeginCollectionItem
  @model LD.Areas.Admin.Models.MySubObject
  
  @using (Html.BeginCollectionItem("MySubObjects"))
  {
      &lt;label&gt;
          @Html.HiddenFor(x =&gt; x.Id)
          @Html.HiddenFor(x =&gt; x.Name)
          @Html.EditorFor(x =&gt; x.IsChecked)@Html.LabelFor(x =&gt; x.IsChecked, Model.Name)
          &lt;text&gt;&amp;nbsp;&lt;/text&gt;
      &lt;/label&gt;
  }
  </code></pre>
  
  <p>ActionResult called by AJAX: <strong>(this is I'm getting null for MyObject)</strong></p>
  
  <pre><code>public ActionResult NewMyObjectSection(MyObject myObject)
  {
      // take passed object values and create a new object based on myObject
  
      var newObject = new MyObject(); 
  
      return PartialView("~/Areas/Admin/Views/Shared/EditorTemplates/MyObject.cshtml", myObject);
  }
  </code></pre>
  
  <h1>What I've Tried</h1>
  
  <p>Some main things that stick out to me are that <code>BeginCollectionItem</code> attaches a <code>GUID</code> to make the collection items unique, for ex, when my <code>&lt;tablebody class="myobject-container"&gt;</code> is serialized, it looks like this (this is the result from my real object, Rates == MyObjects):</p>
  
  <pre><code>"Rates.index=b589808e-3e99-40f2-827c-58feaa9bebc3&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.OrganismNid=0&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.OrganismName=&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.OrganismUpdateType=New&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.IsPest=false&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.IsPreEmergent=false&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.IsPostEmergent=false&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.IsSafeOn=false&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.HasControl=false&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.HasSuppression=false&amp;ControlTypes.index=bd9d1180-9768-4ca4-8316-fc5cbf62ca97&amp;ControlTypes%5Bbd9d1180-9768-4ca4-8316-fc5cbf62ca97%5D.ControlTypeNid=1&amp;ControlTypes%5Bbd9d1180-9768-4ca4-8316-fc5cbf62ca97%5D.Name=Annual&amp;ControlTypes%5Bbd9d1180-9768-4ca4-8316-fc5cbf62ca97%5D.IsChecked=false&amp;ControlTypes.index=0264b842-5cac-4b1a-8b79-8f9a13a26428&amp;ControlTypes%5B0264b842-5cac-4b1a-8b79-8f9a13a26428%5D.ControlTypeNid=2&amp;ControlTypes%5B0264b842-5cac-4b1a-8b79-8f9a13a26428%5D.Name=Biennial&amp;ControlTypes%5B0264b842-5cac-4b1a-8b79-8f9a13a26428%5D.IsChecked=false&amp;ControlTypes.index=6a344551-add6-4e9b-96e2-59a6fc857327&amp;ControlTypes%5B6a344551-add6-4e9b-96e2-59a6fc857327%5D.ControlTypeNid=3&amp;ControlTypes%5B6a344551-add6-4e9b-96e2-59a6fc857327%5D.Name=Winter%20Annual&amp;ControlTypes%5B6a344551-add6-4e9b-96e2-59a6fc857327%5D.IsChecked=false&amp;ControlTypes.index=91f5c6ff-402e-48ae-8f9c-1131511699a8&amp;ControlTypes%5B91f5c6ff-402e-48ae-8f9c-1131511699a8%5D.ControlTypeNid=4&amp;ControlTypes%5B91f5c6ff-402e-48ae-8f9c-1131511699a8%5D.Name=Perennial&amp;ControlTypes%5B91f5c6ff-402e-48ae-8f9c-1131511699a8%5D.IsChecked=false&amp;ControlTypes.index=3248ec07-09a8-4bf0-b99f-a518807d9636&amp;ControlTypes%5B3248ec07-09a8-4bf0-b99f-a518807d9636%5D.ControlTypeNid=5&amp;ControlTypes%5B3248ec07-09a8-4bf0-b99f-a518807d9636%5D.Name=Top%20Growth%20Control%20Only&amp;ControlTypes%5B3248ec07-09a8-4bf0-b99f-a518807d9636%5D.IsChecked=false&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.BroadcastMin=&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.BroadcastMax=&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.BroadcastChemUnitTypeNid=1&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.BroadcastAreaUnitTypeNid=9&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.SpotMin=&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.SpotMax=&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.SpotChemUnitTypeNid=1&amp;Rates%5Bb589808e-3e99-40f2-827c-58feaa9bebc3%5D.SpotFinalSolutionUnitTypeNid=1"
  </code></pre>
  
  <p>I'm not sure if my controller understands to bind that to my object.</p>
  
  <p>I've tried other methods of passing it, such as stripping out GUIDs and extra junk from the string, passing it as a string and deserializing in C#, and others:</p>
  
  <pre><code>var serializedArray = currentArea.find(":input").serializeArray();
  
  //https://www.regextester.com/94410
  var regex = new RegExp("(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}");
  
  var stripped = serializedData.replace(/(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}/g, '');
  
  stripped = stripped.replace(/%5B/g, '[').replace(/%5D/g, ']');
  
  var strung = JSON.stringify({ "modelData": serializedData });
  </code></pre>
  
  <p>At this point I'm at a loss.  This is for a personal passion project and my lack of AJAX / JSON knowledge is really holding me back.  I'm not sure what else to try.  Any pointing in the right direction would be appreciated.</p>