Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model Containing List of Models (MVC-3, Razor)

This problem has been plaguing me for two days now. There are some similar posts, but none that address my problem completely.

Using MVC-3, Razor syntax:

-- EDIT.cshtml --

@using (Html.BeginForm("Edit", "My", FormMethod.Post, new { enctype = "multipart/form-data" })) {     <!-- Some fields... -->     <div class="editor-field">         @Html.TextAreaFor(m => m.LongDescription)         @Html.ValidationMessageFor(m => m.LongDescription)     </div>      <!-- Some more fields work... Including picture upload (summary).-->     <input name="button" type="submit" value="Add Picture" />      <!-- Picture Item display -->     @foreach(var thumbnail in Model.ThumbnailImagePathAndNames)      {       <img src="@Url.Content(@thumbnail.ThumbnailPicturePath)" alt="" width="200" />       @Html.RadioButtonFor(o=>o.SelectedImage, @thumbnail.ImageGUID)  Primary Picture        <!-- Checkbox to mark for deletion -->       @Html.CheckBoxFor(o=>thumbnail.Delete) Delete ???????? <!---- Here is a problem - I don't understand how this should work -->     }     <input id="Submit1" name="button" type="submit" value="Complete Edit!" /> } 

-- MyController.cs --

 [HttpPost]  public ActionResult Edit(String button, HttpPostedFileBase file, MyMainModel model)  {      // if button = submit picture,  work with picture here and break(long story)       // save model data          // if valid, save and redirect        // not valid or error, load up view like normal but with error messages      model.LoadThumbnails();      return View(model);   } 

-- MyMainModel.cs --

public class MyMainModel {     // some properties...      public Guid? SelectedImage { get; set; }      [Display(Name = "Detailed Description")]     public String LongDescription { get; set; }      // some more properties....       // and finally my list of models     public IList<ThumbnailModel> ThumbnailImagePathAndNames { get; set; }      public void LoadThumbnails()     {          // load up initial thumbnail models          this.ThumbnailImagePathAndNames = new List<ThumbnailModel>(readDataService.GetThumbnailModels(this.SomeID));     } } 

-- ThumbnailModels.cs --

public class ThumbnailModel {     public Guid ImageGUID { get; set; }     public String FullSizePicturePath { get; set; }     public String ThumbnailPicturePath { get; set; }      public bool Delete { get; set; } } 

So whats the problem? Well, when the "Complete Edit!" button is pressed, the MyController's Edit is called, as expected with all the MyMainModle's data in tact.... except for the list of ThumbnailModel's - those turn out to be null.

How is this supposed to be done? I have tried many different approaches to this including making an editable template and using EditFor(o=>... all to no avail (this became confusing as I didn't know if the EditFor was supposed to be for the entire collection or just a single item in the collection - I tried both ways). All used to work until I added the complexity of the checkbox for deletion, therefore needing to retrieve the list of ThumbnailModels to check that internal Delete property value.

Thank you all for reading and trying to understand this.

[Disclaimer - some variable and method names have been changed to protect the innocent program. A lot of code has been stripped away and replaced by comment code.]

like image 550
Rob Avatar asked Jan 18 '11 03:01

Rob


People also ask

What model contains in MVC?

What is an MVC model? An MVC model contains all of your application logic that is not contained in a view or a controller. The model should contain all of your application business logic, validation logic, and database access logic.

How many models are there in MVC?

In fact, in ASP.NET MVC, there are three distinct types of model: the domain model, view model and input model.

Can a view have multiple models?

Introduction. In MVC, we cannot use multiple model tag on a view. But many times, we need to pass multiple models from controller to view or we want to show data from multiple model on a view.


1 Answers

Here's an example that I've put to illustrate some concepts:

Model:

public class MyMainModel {     public Guid? SelectedImage { get; set; }     public string LongDescription { get; set; }      public IEnumerable<ThumbnailModel> ThumbnailImagePathAndNames { get; set; }      public HttpPostedFileBase File { get; set; } }  public class ThumbnailModel {     public Guid ImageGUID { get; set; }     public bool Delete { get; set; } } 

Controller:

public class HomeController : Controller {     public ActionResult Index()     {         var model = new MyMainModel         {             // TODO: fetch from the repository instead of hardcoding             ThumbnailImagePathAndNames = new[]              {                 new ThumbnailModel { ImageGUID = Guid.NewGuid() },                 new ThumbnailModel { ImageGUID = Guid.NewGuid() },                 new ThumbnailModel { ImageGUID = Guid.NewGuid() },             }         };         return View(model);     }      [HttpPost]     public ActionResult Index(MyMainModel model)      {         ... the model will be properly bound here     } } 

View:

@model AppName.Models.MyMainModel @{     ViewBag.Title = "Index";     Layout = "~/Views/Shared/_Layout.cshtml"; } @using (Html.BeginForm("index", "home", FormMethod.Post, new { enctype = "multipart/form-data" })) {     <div class="editor-field">         @Html.TextAreaFor(m => m.LongDescription)         @Html.ValidationMessageFor(m => m.LongDescription)     </div>     <input type="file" name="file" />     <!-- Use different names for the upload and complete submit          buttons so that you can distinguish which one was clicked          in the POST action      -->     <input name="upload" type="submit" value="Add Picture" />      @Html.EditorFor(x => x.ThumbnailImagePathAndNames)         <input name="complete" type="submit" value="Complete Edit!" /> } 

Editor template: (~/Views/Home/EditorTemplates/ThumbnailModel.cshtml):

@model AppName.Models.ThumbnailModel <!-- Pass the image id as hidden field --> @Html.HiddenFor(x => x.ImageGUID) @Html.CheckBoxFor(x => x.Delete) 
like image 193
Darin Dimitrov Avatar answered Sep 17 '22 11:09

Darin Dimitrov