View Model posting null properties back to controller
Solution 1
Well if adding the hiddenfor did not work I would try to make it something like this.
@for (int i = 0; i < Model.OrdItems.Count; i++)
{
@Html.DisplayFor(model => model.zipCodeTerritory[i].channelCode)@Html.HiddenFor(model => model.zipCodeTerritory[i].channelCode)
@Html.DisplayFor(model => model.zipCodeTerritory[i].zipCode)@Html.HiddenFor(model => model.zipCodeTerritory[i].zipCode)
}
Solution 2
When we use Html Helper method Html.DisplayFor()
, it just shows the data as a plain text. You can check it by View Page resource in your browser. What you have do is to use Html.HiddenFor
helper method to bind what data you need after post. Or you can also bind data using JQuery.Ajax
NealR
Updated on September 05, 2022Comments
-
NealR over 1 year
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
List
object that is a property of the view model and displayed in a table below the search form.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)
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:
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
New Territory
,New Description
, andNew Effective Date
text boxes.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.
View Model (this is what gets posted back to the controller)
public class ZipCodeIndex { public List<ZipCodeTerritory> 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<ZipCodeTerritory>(); } }
Model (This populates the List object in the View Model)
[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<System.DateTime> LastUpdateDate { get; set; } public int Id { get; set; } }
View
@model Monet.ViewModel.ZipCodeIndex @using(Html.BeginForm("Index", "ZipCodeTerritory", FormMethod.Post)) { <div id="searchBox" class="boxMe"> <div id="zipBox"> @Html.Raw("Zip Code") @Html.TextAreaFor(model => model.searchZip, new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" }) </div> <div id="dateBox"> @Html.LabelFor(model => model.searchDate) @Html.TextBoxFor(model => model.searchDate, new { style="width: 80px;"}) </div> <div id="stateBox"> @Html.LabelFor(model => model.searchState) @Html.TextBoxFor(model => model.searchState, new { style = "width: 25px;" }) <button type="submit">Search</button> </div> </div> <div id="errorStatus"> @ViewBag.ErrorMessage </div> <div style="clear: both;"></div> } <br/> @Html.ActionLink("Create New", "Create") <br/> @if (Model != null) { using(Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post)) { <div id="cloneBox"> @Html.LabelFor(model => model.newTerritory) @Html.TextBoxFor(model => model.newTerritory, new { style="width: 30px;padding-left:10px;"}) @Html.LabelFor(model => model.newDescription) @Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;" }) @Html.LabelFor(model => model.newEffectiveDate) @Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" }) <button type="submit">Clone Selected Items</button> </div> } <table id="thetable" class="tablesorter" > <thead> <th></th> <th>Channel</th> <th>Territory</th> <th>Description</th> <th>State</th> <th>Zip</th> <th>Effective</th> <th>End Date</th> <th>Last Update By</th> <th>Last Update Date</th> <th></th> </thead> <tbody> @foreach (var item in Model.zipCodeTerritory) { <tr> <td>@Html.CheckBoxFor(model => item.Update)</td> <td> @Html.DisplayFor(model => item.ChannelCode) </td> <td> @Html.DisplayFor(model => item.IndDistrnId) </td> <td> @Html.DisplayFor(model => item.DrmTerrDesc) </td> <td> @Html.DisplayFor(model => item.StateCode) </td> <td> @Html.DisplayFor(model => item.ZipCode) </td> <td> @Html.DisplayFor(model => item.EffectiveDate) </td> <td> @Html.DisplayFor(model => item.EndDate) </td> <td> @Html.DisplayFor(model => item.LastUpdateId) </td> <td> @Html.DisplayFor(model => item.LastUpdateDate) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.Id }) </td> </tr> } </tbody> </table> }
Controller
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) && String.IsNullOrWhiteSpace(search.searchDate) && String.IsNullOrWhiteSpace(search.searchState)) { return View(); } //Determine if necessary search criteria has been sent if (String.IsNullOrWhiteSpace(search.searchZip) && 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) && z.EffectiveDate >= 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 < 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) && z.EffectiveDate >= 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) && z.EffectiveDate >= effectiveDate && 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); }
EDIT
I've added the following hidden fields to the second form (that posts to the
Update
method). This will send back the search criteria however theList
object is still empty.@if (Model != null) { using(Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post)) { @Html.HiddenFor(model => model.searchZip) @Html.HiddenFor(model => model.searchDate) @Html.HiddenFor(model => model.searchState) @Html.HiddenFor(model => model.zipCodeTerritory) <div id="cloneBox">