title: View Model posting null properties back to controller
body: <p>On my Index page I have a search form that allows users to pull information based on 3 search criteria. When the the search results return from the controller, they are returned as a <code>List</code> object that is a property of the view model and displayed in a table below the search form. </p>
  
  <p>The user then has the option of selecting which records they would like to alter. They will do this by selecting a check box next to the specified records. Then, using a form that appears below the search box, will enter the new values and hit an "Update" button to save the changes. (we do this in order to allow the user to make mass updates without having to alter the table row by row)</p>
  
  <p>Here is a screen shot to illustrate what I mean. This is what the page will look like after the user has hit the "Search" button: </p>
  
  <p><img src="https://i.stack.imgur.com/MAhuU.png" alt="enter image description here"> </p>
  
  <p>When the user hit's "Search" the form is instructed to post back to the Index method of the controller. When the user hits "Clone Selected Items" the form is instructed to post back to an "Update" method in the same controller. However, the problem is the view model object that gets posted back to the "Update" method is completely null except for the values placed in the <code>New Territory</code>, <code>New Description</code>, and <code>New Effective Date</code> text boxes. </p>
  
  <p>I'm relatively new to ASP MVC so any help/suggestions would be appreciated. Not sure why the View Model will post back from the controller with the same values it was sent there while using the "Search" button but will not do so using the "Clone Selected Items" one. </p>
  
  <p><strong>View Model (this is what gets posted back to the controller)</strong></p>
  
  <pre><code>public class ZipCodeIndex
  {
      public List&lt;ZipCodeTerritory&gt; zipCodeTerritory { get; set; }
      [DisplayName("Zip Code")]
      public string searchZip { get; set; }
      [DisplayName("Effective on this date")]
      public string searchDate { get; set; }
      [DisplayName("State")]
      public string searchState { get; set; }
      [DisplayName("New Territory")]
      public string newTerritory { get; set; }
      [DisplayName("New Description")]
      public string newDescription { get; set; }
      [DisplayName("New Effective Date")]
      public string newEffectiveDate { get; set; }
  
      public ZipCodeIndex() 
      {
          zipCodeTerritory = new List&lt;ZipCodeTerritory&gt;();
      }
  }
  </code></pre>
  
  <p><strong>Model (This populates the List object in the View Model)</strong></p>
  
  <pre><code>[MetadataType(typeof(ZipCodeTerritoryMetaData))]
  public partial class ZipCodeTerritory
  {
      public bool Update { get; set; }
  }
  
  public partial class ZipCodeTerritory
  {
      public string ChannelCode { get; set; }
      public string DrmTerrDesc { get; set; }
      public string IndDistrnId { get; set; }
      public string StateCode { get; set; }
      public string ZipCode { get; set; }
      public System.DateTime EndDate { get; set; }
      public System.DateTime EffectiveDate { get; set; }
      public string LastUpdateId { get; set; }
      public Nullable&lt;System.DateTime&gt; LastUpdateDate { get; set; }
      public int Id { get; set; }
  }
  </code></pre>
  
  <p><strong>View</strong></p>
  
  <pre><code>@model Monet.ViewModel.ZipCodeIndex
  
      @using(Html.BeginForm("Index", "ZipCodeTerritory", FormMethod.Post))
      {
          &lt;div id="searchBox" class="boxMe"&gt;
              &lt;div id="zipBox"&gt;
                  @Html.Raw("Zip Code")
                  @Html.TextAreaFor(model =&gt; model.searchZip, new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" })
              &lt;/div&gt;
              &lt;div id="dateBox"&gt;
                  @Html.LabelFor(model =&gt; model.searchDate)
                  @Html.TextBoxFor(model =&gt; model.searchDate, new { style="width: 80px;"})
              &lt;/div&gt;
              &lt;div id="stateBox"&gt;
                  @Html.LabelFor(model =&gt; model.searchState)
                  @Html.TextBoxFor(model =&gt; model.searchState, new { style = "width: 25px;" })
                  &lt;button type="submit"&gt;Search&lt;/button&gt;
              &lt;/div&gt;
          &lt;/div&gt;
          &lt;div id="errorStatus"&gt;
              @ViewBag.ErrorMessage            
          &lt;/div&gt;
          &lt;div style="clear: both;"&gt;&lt;/div&gt;
      }
  
  &lt;br/&gt;
  @Html.ActionLink("Create New", "Create")
  &lt;br/&gt;
  
  @if (Model != null)
  {
      using(Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
      {
          &lt;div id="cloneBox"&gt;
              @Html.LabelFor(model =&gt; model.newTerritory)
              @Html.TextBoxFor(model =&gt; model.newTerritory, new { style="width: 30px;padding-left:10px;"})
              @Html.LabelFor(model =&gt; model.newDescription)
              @Html.TextBoxFor(model =&gt; model.newDescription, new { style = "width: 250px;padding-left:10px;" })  
              @Html.LabelFor(model =&gt; model.newEffectiveDate)     
              @Html.TextBoxFor(model =&gt; model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })   
              &lt;button type="submit"&gt;Clone Selected Items&lt;/button&gt;                      
          &lt;/div&gt;
      }    
  
      &lt;table id="thetable" class="tablesorter" &gt;
          &lt;thead&gt;
              &lt;th&gt;&lt;/th&gt;
              &lt;th&gt;Channel&lt;/th&gt;
              &lt;th&gt;Territory&lt;/th&gt;
              &lt;th&gt;Description&lt;/th&gt;
              &lt;th&gt;State&lt;/th&gt;
              &lt;th&gt;Zip&lt;/th&gt;
              &lt;th&gt;Effective&lt;/th&gt;
              &lt;th&gt;End Date&lt;/th&gt;
              &lt;th&gt;Last Update By&lt;/th&gt;
              &lt;th&gt;Last Update Date&lt;/th&gt;
              &lt;th&gt;&lt;/th&gt;
          &lt;/thead&gt;
          &lt;tbody&gt;
              @foreach (var item in Model.zipCodeTerritory)
              {
                  &lt;tr&gt;
                      &lt;td&gt;@Html.CheckBoxFor(model =&gt; item.Update)&lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.ChannelCode)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.IndDistrnId)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.DrmTerrDesc)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.StateCode)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.ZipCode)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.EffectiveDate)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.EndDate)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.LastUpdateId)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.DisplayFor(model =&gt; item.LastUpdateDate)
                      &lt;/td&gt;
                      &lt;td&gt;
                          @Html.ActionLink("Edit", "Edit", new { id = item.Id })
                      &lt;/td&gt;
                  &lt;/tr&gt;
              }
          &lt;/tbody&gt;
      &lt;/table&gt;    
  }
  </code></pre>
  
  <p><strong>Controller</strong></p>
  
  <pre><code>    public ViewResult Index(ZipCodeIndex search)
      {
          try
          {
              //If search criteria is null page is loading for the first time so send blank view
              if (String.IsNullOrWhiteSpace(search.searchZip) &amp;&amp;
                  String.IsNullOrWhiteSpace(search.searchDate) &amp;&amp;
                  String.IsNullOrWhiteSpace(search.searchState))
              {
                  return View();
              }
  
              //Determine if necessary search criteria has been sent
              if (String.IsNullOrWhiteSpace(search.searchZip) &amp;&amp; String.IsNullOrWhiteSpace(search.searchState))
              {
                  ViewBag.ErrorMessage = "Either State or Zip Code Must be Specified";
                  return View(search);
              }
  
              DateTime effectiveDate;
  
              //Convert date string to DateTime type
              if (String.IsNullOrWhiteSpace(search.searchDate))
              {
                  effectiveDate = DateTime.MinValue;
              }
              else
              {
                  effectiveDate = Convert.ToDateTime(search.searchDate);
              }
  
              //Conduct search by State Code/Date alone
              if (String.IsNullOrWhiteSpace(search.searchZip))
              {
                  search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                             where z.StateCode.Equals(search.searchState) &amp;&amp;
                                                   z.EffectiveDate &gt;= effectiveDate
                                             select z).ToList();
                  return View(search);
              }
  
              //Zip codes have been requested to conduct zip search
              string[] zipArray;
  
              //Create array and remove white spaces
              zipArray = search.searchZip.Split(',').Distinct().ToArray();
              for (int i = 0; i &lt; zipArray.Length; i++)
              {
                  zipArray[i] = zipArray[i].Trim();
              }
  
              //Determine if state code is being used in search
              if (String.IsNullOrWhiteSpace(search.searchState))
              {
                  foreach (var zip in zipArray)
                  {
                      var item = from z in db.ZipCodeTerritory
                                 where z.ZipCode.Equals(zip) &amp;&amp;
                                        z.EffectiveDate &gt;= effectiveDate
                                 select z;
                      search.zipCodeTerritory.AddRange(item);
                  }
              }
              else
              {
                  foreach (var zip in zipArray)
                  {
                      var item = from z in db.ZipCodeTerritory
                                 where z.ZipCode.Equals(zip) &amp;&amp;
                                        z.EffectiveDate &gt;= effectiveDate &amp;&amp;
                                        z.StateCode.Equals(search.searchState)
                                 select z;
                      search.zipCodeTerritory.AddRange(item);
                  }
              }
          }
          catch (DbEntityValidationException dbEx)
          {
              ViewBag.ErrorMessage = "An error has occurred, we apologize for the incovenience. IT has been notified and will resolve the issue shortly.";
              SendEmail.ErrorMail(Common.ErrorCheck.CombineDbErrors(dbEx));
          }
          catch (Exception ex)
          {
              ViewBag.ErrorMessage = ErrorCheck.FriendlyError(ex);
              SendEmail.ErrorMail(ex);
          }
  
          return View(search);
      }
  
  
      [HttpPost]
      public ActionResult Update(ZipCodeIndex updateZip)
      {
          foreach (var zipCode in updateZip.zipCodeTerritory)
          {
              if (zipCode.Update)
              {
                  try
                  {
                      if (!string.IsNullOrEmpty(updateZip.newTerritory)) zipCode.IndDistrnId = updateZip.newTerritory;
                      if (!string.IsNullOrWhiteSpace(updateZip.newDescription)) zipCode.DrmTerrDesc = updateZip.newDescription;
                      if (!string.IsNullOrWhiteSpace(updateZip.newEffectiveDate)) zipCode.EffectiveDate = Convert.ToDateTime(updateZip.newEffectiveDate);
  
                      db.Entry(zipCode).State = EntityState.Modified;
                      db.SaveChanges();
                  }
                  catch (DbEntityValidationException dbEx)
                  {
                      ViewBag.ErrorMessage = "An error has occurred, we apologize for the incovenience. IT has been notified and will resolve the issue shortly.";
                      SendEmail.ErrorMail(Common.ErrorCheck.CombineDbErrors(dbEx));
                  }
                  catch (Exception ex)
                  {
                      ViewBag.ErrorMessage = "An error has occurred, we apologize for the incovenience. IT has been notified and will resolve the issue shortly.";
                      SendEmail.ErrorMail("Zip Code not updated: " + zipCode.ToString() + " |MESSAGE| " + ex.Message);
                  }
              }
          }
  
          return RedirectToAction("Index", updateZip);
      }
  </code></pre>
  
  <p><strong>EDIT</strong></p>
  
  <p>I've added the following hidden fields to the second form (that posts to the <code>Update</code> method). This will send back the search criteria however the <code>List</code> object is still empty. </p>
  
  <pre><code>@if (Model != null)
  {
      using(Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
      {
          @Html.HiddenFor(model =&gt; model.searchZip)
          @Html.HiddenFor(model =&gt; model.searchDate)
          @Html.HiddenFor(model =&gt; model.searchState)
          @Html.HiddenFor(model =&gt; model.zipCodeTerritory)
  
          &lt;div id="cloneBox"&gt;
  </code></pre>