Sean Holmesby
Sean Holmesby

Reputation: 2145

MVC 3: DropDownList on an Edit Form for an object that is a property of a ViewModel

Overview: I'm trying to use a ViewModel that has a property that is the model class that I'm trying to edit. I've seen the edit form work using the MVC scaffolding edit forms when editing a model directly, however I am trying to use a ViewModel that contains the model being edited. Everything works except for the saving of a field that's displayed in a DropDownList.

Explanation: I've attempted to use the scaffolding features of MVC 3 to create an edit form for a model. In the MVC Music Store tutorial, this is done for the Edit page of an Album, in the StoreManagerController. Within that page, they have two drop downs for Genre and Artist. Each looks similar to this in the view:-

<div class="editor-label">
    @Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
    @Html.DropDownList("GenreId", String.Empty)
    @Html.ValidationMessageFor(model => model.GenreId)
</div>

As far as I can tell, these have their options filled out in the controller using the ViewBag.

ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);

Working with the Model directly

In my code, I managed to do the same with an object that's being saved to the DB through Entity Framework.

Model

public class Season
{
    public int SeasonId { get; set; }
    public int ClubId { get; set; }
    public Club Club { get; set; }
}

Code in Controller

ViewBag.ClubId = new SelectList(clubs, "ClubId", "Name", season.ClubId);

View

<div class="editor-label">
    @Html.LabelFor(model => model.ClubId, "Club")
</div>
<div class="editor-field">
    @Html.DropDownList("ClubId", String.Empty)
    @Html.ValidationMessageFor(model => model.ClubId)
</div>

This works fine.

Working with the View Model

However now I have realised that the page needs more text displayed than what is available in the model I'm editing. I was hoping to create a special View Model, with the edited model (season) and the extra text I want to display.

ViewModel

public class EditSeasonViewModel
{
    public string PlayerName {get; set;}
    public string SummaryText {get; set;}
    public int PlayerId {get; set;}
    public Season season {get; set;}
}

I did this, changed the controller to have the HttpGet and HttpPost methods use the new ViewModel, changed the View to accept the new ViewModel, and changed the all 'EditorFor' methods in the view to use model.season.MyProperty.

Code in Controller

ViewBag.ClubId = new SelectList(clubs, "ClubId", "Name", seasonVM.season.ClubId);

Code in View

<div class="editor-label">
    @Html.LabelFor(model => model.season.ClubId, "Club")
</div>
<div class="editor-field">
    @Html.DropDownList("ClubId", String.Empty)
    @Html.ValidationMessageFor(model => model.season.ClubId)
</div>

When debugging the HttpPost method, all of the values for season properly exist, except for the ClubId value which should come from the DropDropList.

I haven't changed the DropDownList in the View at all from the way it was when we were using the Model directly.

Question: My question is, what do I need to change to get the ClubId to be saved properly when using this new EditSeasonViewModel?

Also, how does the ViewBag.ClubId in the HttpGet method in the controller match to the DropDownList in the View, and then have it's value passed back to the HttpPost method?

Upvotes: 7

Views: 26577

Answers (3)

Antonio Santise
Antonio Santise

Reputation: 300

Please be sure that in your edit/creat action controller, the property is correctly binded:

public async Task<ActionResult> Edit([Bind(Include = "SeasonId,ClubId, otherproperties"]){
// code ...
}

Upvotes: 0

Shyju
Shyju

Reputation: 218892

Avoid using dynamic stuff like ViewBag/ViewData to transfer data from action methods to views. Switch to the strongly typed approach.

Update your Viewmodel to have 2 more properties. one to hold a collection of available clubs and one to hold the selected club.

public class EditSeasonViewModel
{
    public List<SelectListItem> Clubs { set;get;}
    public int SelectedClub { set;get;}

    public string PlayerName {get; set;}
    public string SummaryText {get; set;}
    public int PlayerId {get; set;}
    public Season season {get; set;}

    public EditSeasonViewModel()
    {
        Clubs=new List<SelectListItem>();
    }
}

Now in your GET action, Get the clubs and load it to the Clubs Property.

public ActionResult create(int id)
{
  var vm=new EditSeasonViewModel();
  vm.Clubs=GetListOfSelectListItemFromClubsFromSomeWhere();
  return View(vm);
}

assuming GetListOfSelectListItemFromClubsFromSomeWhere is a method which returns a list of SelectListItem for your Clubs

public List<SelectListItem> GetListOfSelectListItemFromClubsFromSomeWhere()
{
  // to do : return List<SelectListItem> with Value and Text(ClubId Id & Name)
}

and in your view, use the Html.DropDownListFor helper method

@model EditSeasonViewModel
@using(Html.Beginform())
{

  @Html.DropdownlistFor(x=>x.SelectedClub,
                          new SelectList(Model.Clubs,"Value","Text"),"select")
  <input type="submit" />
}

When this form is being posted, you will get the selected club's id in the SelectedClub property of your posted model.

[HttpPost]
public ACtionResult Create(EditSeasonViewModel model)
{
  if(ModelState.IsValid)
  {
   int clubId= model.SelectedClub;
   //to do : save and redirect (PRG pattern)
  }
  vm.Clubs=GetListOfSelectListItemFromClubsFromSomeWhere();
  return View(model);
}

Upvotes: 3

Mathew Thompson
Mathew Thompson

Reputation: 56459

It's your DropDownList declaration that's incorrect. Try this instead:

@Html.DropDownListFor(m => m.season.ClubId, ViewBag.ClubId)

But really, you should put that select list in your model:

public SelectList Clubs { get; set; }

Then populate it in your controller:

Model.Clubs = new SelectList(clubs, "ClubId", "Name", season.ClubId);

Then you can do:

@Html.DropDownListFor(m => m.season.ClubId, Model.Clubs)

Upvotes: 12

Related Questions