We need to select a model reference from a view. To do this we need to add a dropdownlistfor. This is based on a selectlist containing database models.
passing selectlists by viewbag
This solves our problem, but we do not like using ViewBag:
public ActionResult Create()
{
ViewBag.SomeModels = new SelectList(context.SomeModel, "id", "modelDescription");
return View();
}
Our solution
We added a context to a viewmodel to do the same, but this is MVC anti-pattern:
public class SomeModelViewModel : SomeModel
{
private SomeContext context = new SomeContext ();
public SelectList getSomeOtherModels()
{
return new SelectList(context.SomeOtherModel, "id", "modelDescription");
}
}
Does anyone have suggestions for a clean way to solve this problem?
1 Answer 1
I think there are a few things that can be fixed here, but overall I don't think this is automatically an anti-pattern. It could just be executed a little better.
Caveat: I don't practice what I preach nearly as often as I should.
What I tend to do in my own projects, and the standard where I work, is for the view model to expose an IEnumerable<SelectListItem>
(actually, when we're lazy, we put this enumerable in the ViewBag like you have but we're trying to get away from that.) Rather than giving the context to your view model, just give the items to the view model when it's constructed and have it generate the SelectListItem
s from them. AFAIK, that's the controller's job.
public class SomeViewModel
{
public IEnumerable<SelectListItem> ListItems { get; private set; }
public SomeViewModel(IEnumerable<SomeModel> items)
{
ListItems = items
.Select(i => new SelectListItem()
{
Text = i.Name,
Value = i.Id,
Selected = i.IsSelected // or whatever
})
.ToList();
}
}
public class SomeController
{
public ActionResult Index()
{
var viewModel = new SomeViewModel(_context.SomeModels);
return View(viewModel);
}
}
Again, I can't promise this is the more "correct" way to do it, but I've done it and seen it done on at least four different projects, and it's worked well for us so far.
P.S. Your ViewModel probably shouldn't inherit from your Model. I've never actually seen that done before, but some cursory Googling shows that it's not really recommended. You should have the exact properties you need in the view copied into your view model (I hear there are tools for his), and use a tool like AutoMapper to copy values back and forth when necessary.
-
1\$\begingroup\$ I love the idea that the ViewModel requires the list so make it to the constructor. Never thought of that and actually I liked it A LOT. Thanks. \$\endgroup\$Bart Calixto– Bart Calixto2013年02月25日 23:32:18 +00:00Commented Feb 25, 2013 at 23:32
-
1\$\begingroup\$ and what you do in
[HttpPost]
actions, how do you get these values again? \$\endgroup\$ebram khalil– ebram khalil2014年06月11日 16:40:10 +00:00Commented Jun 11, 2014 at 16:40
SelectList
? Can't you have the items for the select list in one Property, and the selected value in another? \$\endgroup\$