Reputation: 2332
I have a many-to-many
field in Menu model named Roles
, i'm trying to create a 'create' view for the model.
my create action methods:
public ActionResult Create()
{
ViewBag.ParentMenuId = new SelectList(_db.Menus, "Id", "Name");
ViewBag.Roles = new SelectList(_db.UserRoles.ToList(), "Id", "Name");
return View();
}
[HttpPost]
public ActionResult Create(Menu menu)
{
if (ModelState.IsValid)//the state is always **invalid**
{
_db.Menus.Add(menu);
_db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ParentMenuId = new SelectList(_db.Menus, "Id", "Name", menu.ParentMenuId);
return View(menu);
}
my view(Create.cshtml):
<div class="editor-label">
@Html.LabelFor(model => model.Roles, "Roles")
</div>
<div class="editor-field">
@Html.ListBox("Roles")
@Html.ValidationMessageFor(model => model.Roles)
</div>
how to fix it? I'm getting the following error:
The ViewData item that has the key '' is of type '' but must be of type 'IEnumerable<SelectListItem>'
Upvotes: 0
Views: 189
Reputation: 60493
Well, the error message is rather clear, you nead a IEnumerable<SelectListItem>
So change your ViewBag.Roles definition from
new SelectList(_db.UserRoles.ToList(), "Id", "Name");
to
_db.UserRoles.ToList().Select(m => new SelectListItem { Value=m.Id, Text=m.Name});
Edit
In your view :
@Html.ListBox("Roles", (IEnumerable<SelectListItem>)ViewBag.Roles)
Edit2
You have a model binding problem
In your view
@Html.ListBox("selectedRoles", (IEnumerable<SelectListItem>)ViewBag.Roles)
in your Post Action, try to add a new parameter :
[HttpPost]
public ActionResult Create(Menu menu, int[] selectedRoles)
and treat each selected role "manually" in your action code.
EDIT
ViewModel sample (not working as is) In your view, you need
a DropDownList (ParentMenuId)
a ListBox (RoleIDs)
the result of this listBox (model binding for the post action)
other properties of the Menu class (maybe not all)
The idea would be to create a
public class MenuViewModel
{
public IEnumerable<SelectListItem> ParentMenuList {get;set;}//a dropDown
public IEnumerable<SelectListItem> RoleList {get;set;}//a listBox
public string Name {get;set;}//the menu name //the menu name
public List<int> SelectedRoles {get;set;}
}
then your get Action would be
public ActionResult Create()
{
var model = new MenuViewModel();
model.ParentMenuList = new SelectList(_db.Menus, "Id", "Name");
model.RoleList = new SelectList(_db.UserRoles.ToList(), "Id", "Name");
return View(model);
}
your view would have MenuViewModel... as model
@model MenuViewModel
//your other code
//the listbox
<div class="editor-label">
@Html.LabelFor(model => model.SelectedRoles, "Roles")
</div>
<div class="editor-field">
@Html.ListBoxFor(m => m.SelectedRoles, Model.RoleList)
@Html.ValidationMessageFor(model => model.Roles)
</div>
then your POST action would become
[HttpPost]
public ActionResult Create(MenuViewModel model)
{
if (ModelState.IsValid)
{
var menu = new Menu {Name = model.Name };//for example
menu.Roles = _db.UserRoles.Where(rl => model.SelectedRoles.Contains(rl.Id)).ToList();
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
Pro ViewModel :
you're just using the properties you need.
you don't need ViewBag (which is nice to avoid : it's dynamic, not strongly typed, so... hard to test, refactoring problems, etc.)
everything is in your view's model
Cons ViewModel :
Upvotes: 4
Reputation: 7385
I was create same project but i don't get that error.
It seems model of listbox was bound to another type, try to specify explicitly viewbag model like this:
@Html.ListBox("Roles",ViewBag.Roles as SelectList)
And in [HttpPost] method try to add ViewBag.Roles which exists in Create - get method, like this:
[HttpPost]
public ActionResult Create(Menu menu)
{
if (ModelState.IsValid)//the state is always **invalid**
{
_db.Menus.Add(menu);
_db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ParentMenuId = new SelectList(_db.Menus, "Id", "Name", menu.ParentMenuId);
ViewBag.Roles = new SelectList(_db.UserRoles.ToList(), "Id", "Name");
return View(menu);
}
Upvotes: -1