Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ASP.NET MVC 3 with Razor, what's the most effective way to add an ICollection to a Create view?

I'm using Entity Framework Code First to generated my database, so I have an object defined like the following:

public class Band
{
    public int Id { get; set; }
    [Required(ErrorMessage = "You must enter a name of this band.")]
    public string Name { get; set; }
    // ...
    public virtual ICollection<Genre> Genres { get; set; }
}

Now I'm looking at a create view for this and the default scaffolding isn't adding Genres to my form, which from past experience is about what I expect.

Looking online I've found Using ASP.NET MVC v2 EditorFor and DisplayFor with IEnumerable<T> Generic types which seems to come closest to what I want, but doesn't seem to make sense with Razor and possibly MVC 3, per ASP.NET MVC 3 Custom Display Template With UIHint - For Loop Required?.

At present I've added the listing of genres to the ViewBag and then loop through that listing in my create view:

@{
    List<Genre> genreList = ViewBag.Genres as List<Genre>;
}
// ...
<ul>
@for (int i = 0; i < genreList.Count; i++)
{
    <li><input type="checkbox" name="Genres" id="Genre@(i.ToString())" value="@genreList[i].Name" /> @Html.Label("Genre" + i.ToString(), genreList[i].Name)</li>
}
</ul>

Outside of not yet handling cases where the user has JavaScript disabled and the checkboxes need to be re-checked, and actually updating the database with this information, it does output the genres as I'd like.

But this doesn't feel right, based on how good MVC 3 has become.

So what's the most effective way to handle this in MVC 3?

like image 987
James Skemp Avatar asked Aug 29 '11 12:08

James Skemp


1 Answers

I don't send lists into my View via the ViewBag, instead I use my viewmodel to do this. For instance, I did something like this:

I have an EditorTemplate like this:

@model IceCream.ViewModels.Toppings.ToppingsViewModel
<div>
   @Html.HiddenFor(x => x.Id)
   @Html.TextBoxFor(x =x> x.Name, new { @readonly="readonly"})
   @Html.CheckBoxFor(x => x.IsChecked)
</div>

which I put in my Views\IceCream\EditorTemplates folder. I use this to display some html for allowing the user to "check" any particular topping.

Then in my View I've got something like this:

@HtmlEditorFor(model => model.Toppings)

and that will use that result in my EditorTemplate being used for each of the toppings in the Toppings property of my viewmodel.

And then I've got a viewmodel which, among other things, includes the Toppings collection:

public IEnumerable<ToppingsViewModel> Toppings { get; set; }

Over in my controller, among other things, I retrieve the toppings (however I do that in my case) and set my viewmodel's property to that collection of toppings. In the case of an Edit, where toppings may have been selected previously, I set the IsChecked member of the TopingsViewModel and it'll set the corresponding checkboxes to checked.

Doing it this way provided the correct model binding so that when the user checked a few toppings, the underlying items in the collection reflected those selections. Worked well for me, hope it's helpful for you.

like image 121
itsmatt Avatar answered Sep 29 '22 07:09

itsmatt