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?
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.
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)
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With