Scott 混合理论
Scott 混合理论

Reputation: 2332

How do you code a "Create" view for a model?

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

Answers (2)

Rapha&#235;l Althaus
Rapha&#235;l Althaus

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 :

  • you have to map your Model to your ViewModel in the get action, and map from ViewModel to Model in post action, but... it's worse the price !

Upvotes: 4

testCoder
testCoder

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

Related Questions