tags:

views:

61

answers:

2

I have 3 many-many tables: Users -< UserRoles >- Roles. I set up my model like this:

public class User
{
    public int UserId {get; set;};
    public IEnumerable<Role> Roles {get; set;};
}

public class Role
{
    public int RoleId {get; set;};
    public string RoleName {get; set};
}

public class UserDisplayModel
{
    public User User{get; set;};
    public IEnumerable<Role> AllRoles {get; set;}
}

When editing/creating the user, how do I go about getting the checked checkbox of the roles in the controller and how would I set this up in my view?

If i'm wrong from the start on the way I set up my Model, please tell me and assist on how I would go about doing this.

Thanks.

A: 

You could build a custom model binder. Read here for an example.

eKek0
How would I go about putting `AllRoles` on the view as checkboxes then get them in the Model on return?
Shawn Mclean
+1  A: 

The key is that you need your collection properly rendering in the view. First off, add a Boolean property to the Role view data object so we have something to bind our check box to:

public class Role
{
    public bool IsInRole { get; set; }
    [HiddenInput(DisplayValue = false)]
    public int RoleId { get; set; }
    [HiddenInput(DisplayValue = true)]
    public string RoleName { get; set; }
}

Notice I put some HiddenInput attribute on the properties (more on that later). Also you could pass your User object as shown above to the view - this already has the child collection of Roles. There are a few ways to render this collection in the view, but one of the easiest is:

<%: Html.EditorFor(m => m.Roles) %>

Now add an editor template for the Role object so the line above does what we want. Add Role.ascx to /Views/Shared/EditorTemplates folder. Roles.ascx can looking something like this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcApplication5.Controllers.Role>" %>
<%: Html.EditorFor(m => m.IsInRole) %>
<%: Html.EditorFor(m => m.RoleId) %>
<%: Html.EditorFor(m => m.RoleName) %>

You'll see when you do a view source that your html looks something like this:

<input class="check-box" id="Roles_0__IsInRole" name="Roles[0].IsInRole" type="checkbox" value="true" /><input name="Roles[0].IsInRole" type="hidden" value="false" />
<input id="Roles_0__RoleId" name="Roles[0].RoleId" type="hidden" value="1" />
RoleName1<input id="Roles_0__RoleName" name="Roles[0].RoleName" type="hidden" value="RoleName1" />
<input class="check-box" id="Roles_1__IsInRole" name="Roles[1].IsInRole" type="checkbox" value="true" /><input name="Roles[1].IsInRole" type="hidden" value="false" />
<input id="Roles_1__RoleId" name="Roles[1].RoleId" type="hidden" value="2" />
RoleName2<input id="Roles_1__RoleName" name="Roles[1].RoleName" type="hidden" value="RoleName2" />

This is key for model binding when your form is posted back. We used DisplayValue=true for the display name because we need the hidden input for post back model binding, but it's need to be read-only. for the roleId, that is a hidden input and no value is displayed to the user. See this post for more information on the HiddenInput.

When you post this back, MVC built-in model binder will ensure that your roles collection is constructed property and you'll see the check boxes state properly reflected in your model.

Steve Michelotti
Your Roles.ascx takes only Role, `<%: Html.EditorFor(m => m.Roles) %>` is a list. If i make Roles.ascx take a list, I have to loop over it, then it does not contain the index of the array in the view source.
Shawn Mclean
Actually, that is the beauty of it. The built-in <%: Html.EditorFor(m => m.Roles) %> is smart enough to recognize that it is being passed a collection. Then it sees the Roles.ascx editor template and it uses it for each item in the collection. In effect, the EditorFor() template is doing the looping for you. It works. I implemented your example last night and all the above code I posted is from working bits. Give it a shot and you'll see it works as you need.
Steve Michelotti