Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 895
Sean Holmesby Avatar asked Mar 20 '13 12:03

Sean Holmesby


People also ask

How do I populate a DropDownList based on another dropdown selected value?

You will have to simply execute the script named CreateCascadingDatabase. sql stored in the SQL Folder of the attached sample and it will create the complete database with data. Below is the HTML Markup which contains three ASP.Net DropDownList controls each for Country, State and City.


2 Answers

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)
like image 164
mattytommo Avatar answered Sep 30 '22 12:09

mattytommo


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);
}
like image 39
Shyju Avatar answered Sep 30 '22 13:09

Shyju