List of non-model objects on the back in asp.net mvc 4

In my MVC 4 application, I have a Client that can have several sites and can subscribe to several service packages. A short version of my view model looks below

public class SubscriptionModel { public int MemberId { get; set; } public List<SitePackage> SitePackges { get; set; } public SubscriptionModel() { SitePackges=new List<SitePackage>(); } } public class SitePackage { public int SiteId { get; set; } public List<PackageDisplayItem> LstPackageDisplayItems { get; set; } public SitePackage() { LstPackageDisplayItems=new List<PackageDisplayItem>(); } } public class PackageDisplayItem { public int PackageId { get; set; } [Display(Name = "Package")] public string Name { get; set; } [DataType(DataType.Date)] [Display(Name = "Start Date")] public DateTime? StartDate { get; set; } } 

In my controller, I populate the model and then move on to the View Model for rendering

  @using (@Html.BeginForm("CalculateCost", "HelpDesk", FormMethod.Post, new { @class = "form", id = "PackageSubscription", name = "PackageSubscription" })) { @Html.HiddenFor(x=>x.MemberId) <table class="table"> @foreach (var site in Model.SitePackges) { <input name="SiteId" id="SiteId" type="hidden" value=@site.SiteId.ToString() /> <tr><td class="col-sm-3">@site.SiteId</td></tr> <tr> <th class="col-sm-3"> Name </th> <th class="col-sm-2"> Start Date </th> </tr> @Html.Partial("_Packages",site.LstPackageDisplayItems) } 

My partial view is similar to

 @model List<PackageDisplayItem> @for (int i = 0; i < Model.Count; i++) { @Html.HiddenFor(x => x[i].PackageId) <tr id="@Model[i].PackageId"> <td> @Html.DisplayFor(x => x[i].Name) </td> <td> @Html.TextBoxFor(x => x[i].StartDate, "{0:d MMM yyyy}", new { @class = "jquery_datepicker form-control", autocomplete = "off" }) </td> </tr> } 

Every thing looks great, but on the form blank, model binding is not a mandatory SitePackges list, and its score is always 0. My controller has the following signatures.

 [HttpPost] public ActionResult CalculateCost(SubscriptionModel subscriptionModel ) { var receivedModel = subscriptionModel; } 

I'm not sure that the model I developed is the best approach to solving this requirement (the requirement is to show one site, and below it shows the packages, and then the second site and packages, etc.). The controls seem to have unique indexes.

JQuery Post

 function SubmitForm() { console.log($("#PackageSubscription").serialize()); $.ajax({ url: '/HelpDesk/CalculateCost', cache: false, dataType: 'json', data: $("#PackageSubscription").serialize(), type: 'POST', success: function (data) { } }); 

}

I would be grateful for any help. thanks

+8
jquery asp.net-mvc asp.net-mvc-4
source share
2 answers

The current implementation is the inputs that look like this:

 <input ... name="[0].Name" .../> <input ... name="[1].Name" .../> 

but to bind the model to you, they should look like this:

 <input ... name="SitePackges[0].LstPackageDisplayItems[0].Name" .../> <input ... name="SitePackges[0].LstPackageDisplayItems[1].Name" .../> <input ... name="SitePackges[1].LstPackageDisplayItems[0].Name" .../> <input ... name="SitePackges[1].LstPackageDisplayItems[1].Name" .../> 

A : you need to visualize the controls in nested for loops

 for(int i = 0; i < Model.SitePackges.Count; i++) { @Html.HiddenFor(m => m.SitePackges[i].SiteId) for(int j = 0; j < Model.SitePackges[i].LstPackageDisplayItems.Count; j++) { @Html.TextBoxFor(m => m.SitePackges[i].LstPackageDisplayItems[j].Name) } } 

B : use custom EditorTemplates for model types

Views / Shared / EditorTemplates / SitePackage.cshtml

 @model SitePackage @Html.HiddenFor(m => m.SiteId) @Html.EditorFor(m => m.LstPackageDisplayItems) 

Views / Shared / EditorTemplates / PackageDisplayItem.cshtml

 @model PackageDisplayItem @Html.TextBoxFor(m => m.Name) 

and in the main view

 @model SubscriptionModel @using (@Html.BeginForm()) { @Html.HiddenFor(m => m.MemberId) @Html.EditorFor(m => m.SitePackges) <input type="submit" /> } 
+9
source share

I found that you need to have the item id in the parent panel.

 int pos = Model.Products.Count - 1; for (int j = 0, a = Model.Products.Count; j < a; j++) { // I exclude the last one because the users is adding it // and will be displayed in another section. else causes problems if (j != pos) { <div> @Html.HiddenFor(m => m.Products[j].Id) <span> @Html.DisplayTextFor(m => m.Products[j].Description) @Html.HiddenFor(m => m.Products[j].Description) </span> <span> @Html.DisplayTextFor(m => m.Products[j].Style) @Html.HiddenFor(m => m.Products[j].Style) </span> </div> } } 

this one didn't work below

 for (int j = 0, a = Model.Products.Count; j < a; j++) { if (j != pos) { <div> @Html.HiddenFor(m => m.Products[j].Id) @Html.HiddenFor(m => m.Products[j].Description) @Html.HiddenFor(m => m.Products[j].Style) </div> } } 

or

 for (int j = 0, a = Model.Products.Count; j < a; j++) { if (j != pos) { <div> <span> @Html.HiddenFor(m => m.Products[j].Id) </span> <span> @Html.DisplayTextFor(m => m.Products[j].Description) @Html.HiddenFor(m => m.Products[j].Description) </span> <span> @Html.DisplayTextFor(m => m.Products[j].Style) @Html.HiddenFor(m => m.Products[j].Style) </span> </div> } } 
0
source share

All Articles