I'm currently working on a project in which we have a number of similar models. We made them all inherit from the same base model and we also made a common controller (basic stuff like list, adding, editing etc.) handles the base model. It actually works and made things easy for us, but I have small problem with dropdowns.
Without going into much details, it would make things really easy for me if I could pass data from controller to view using an action. Something like this:
@Html.DropDownListFor(model => model.Xtypechild, Action("GetList", "XController"))
Where action GetList
returns IEnumerable<SelectListItem>
This way I could write the code just once and use it for a number of different controllers.
I know it sounds crazy, but if I tried use one of more conventional ways to do this I would end up with redundant code again and would lose some of the advantage we gained from inheriting controllers.
The only alternative I can see right now is to make GetList
return JSON with all the items and populate the dropdowns using Ajax/JS. I could also make an action that returns a partial view containing the dropdown, but I'm not sure if that's a good idea.
Thank you far all the input. In the end I went with the following solution:
We had a services class for handling database operations for our models. I just add a method that generates a IEnumerable<SelectListItem>
to the base services class. Now I pass the items for drop downs through ViewBag (though it forces me to cast them back to IEnumerable before use in the view). I assign them to ViewBag in the overridden constructor of the inherited controller.
I've best consult the original author of the code if this solution will be ok for our project, though he is on holiday right now. For now this will have to do. On the up side this method produced little code, so it should be easy to replace (if I have to do so).
The code evolved a little bit and now I have something like this in the view (note the int?
casting is there because the property was of type int and it wouldn't work otherwise):
Html.DropDownListFor(
model => model.XId,
new SelectList((List<X>)ViewBag.Xs, "Id", "Name", (Model != null ? (int?)Model.XId : null)),
" -- select -- ",
null)
And something like this inside of overridden constructor of the controller:
ViewBag.Xs = db.Xs.ToList();
It's definitelly not the best way to implement dropdowns in razor, but it was the best way to implement them in the project. The reason for this was: there was no time to make the project use ViewModels and override all the actions of base controller to handle them (as well as provide data for the dropdowns in those actions).
The code evolved even further. It's certainly not a good idea to execute queries in the controller constructor if the data will be used just in a handful of views. So now the constructor contains equivalent of:
ViewBag.Xs = (IQueryable<X>)db.Xs;
And the view:
Html.DropDownListFor(
model => model.XId,
new SelectList((IQueryable<X>)ViewBag.Xs, "Id", "Name", (Model != null ? (int?)Model.XId : null)),
" -- select -- ",
null)
This way the query is only executed when a view actually needs the data.
Items for drop down lists should definitely be in your ViewModel. I don't think it's wrong to create select lists in your derived ViewModels. It might seem like duplicate code, but at least it's clear what you do. You can create some nice helpers in your base controller which takes care of mapping entities to
SelectListItem
s.If you really want a generic solution, you could make a method in your base model:
And use this in
DropDownListFor()
:If you want to check example , please enter to link.
http://peternewhook.com/2013/02/asp-net-mvc-selectlist-selectedvalue-dropdownlistfor/
I certainly don't agree with having a method on the view model to populate the
SelectList
, even if it is calling the controller. The reason for that is simple: it is the controller's responsibility to populate a model, not the other way around.The best way to handle this is by having a method on your controller which can build lists for you. As it's a pretty common task, that suggests multiple controllers would need access to that functionality, and so creating your own
BaseController
which all controllers derive from, is the way to go.With that in mind, here's a
BaseController
with a generic method I wrote to build anySelectList
:BaseController:
Note the use of the
NonActionAttribute
marking the method. This prevents the method being invoked as an action. Below is a simple example of how to use this.StudentViewModel:
CourseId
represents the selected course.Course:
HomeController
You'd then display the dropdown in your view like so:
I have a
BaseController
which inherits fromController
and then put methods such asGenerateStateOptions
in theBaseController
.It might look something like this:
Then in your Controller, you can do something like: