MVC CheckBoxList model binding with non boolean
Asked Answered
M

4

5

I want to bind a List to CheckBox and get the selected values. I need to display two such Checkbox tables and have to retrieve both the IDs.

Below is my ViewModel

public partial class ViewModel_Role_Security
{
    public List<Roles> oRoleMaster { get; set; }
    public List<Securities> oSecurityMaster { get; set; }

}

All these three have these two values 1. ID 2. Name (In this case for Role - ID, RoleName | for Securities - ID, SecurityName ...) //add 3rd property of type bool isselected in order to work eith checkboxes only then you will get it posted back These don't have any boolean values

By using this ViewModel I'm binding these items using the below method...

public ActionResult AddingRoleSecurity()
{        
    ListRoles = new List<Roles>();
    ListSecurities = new List<Securities>();  //And then populate them with data ...

    var model = new ViewModel_Role_Security();
    model.oRoleMaster = ListRoles;
    model.oSecurityMaster = ListSecurities;
    return View(model);
}

My corresponding cshtml file is..

@model KnackWFM.BusinessEntities.ViewModel_Role_Security

@using (Html.BeginForm())
{
    <div class="contentsecurity">

        <div class="User_role">
            <p class="Security_role">User Role</p>

            @for (int i = 0; i < Model.oRoleMaster.Count; i++)
            {
                <input id="@Model.oRoleMaster[i].RoleID" name="@Model.oRoleMaster[i].RoleName" type="checkbox" value="@(Model.oRoleMaster[i].RoleName)" />
                <label for="@Model.oRoleMaster[i].RoleID">@Model.oRoleMaster[i].RoleName</label>
                <br />
                @Html.CheckBoxFor(Model.oRoleMaster[i].RoleID.selec)
            }

        </div>

        <div class="User_Page">
            <p class="Security_role">Role Security</p>

            @for (int i = 0; i < Model.oSecurityMaster.Count; i++)
            {
                <input id="@Model.oSecurityMaster[i].SecurityID" name="@Model.oSecurityMaster[i].SecurityName" type="checkbox" value="@(Model.oSecurityMaster[i].SecurityName)" />
                <label for="@Model.oSecurityMaster[i].SecurityID">@Model.oSecurityMaster[i].SecurityName</label>
                <br />
            }

        </div>
        <div class="bottombuttonsecurity">
            <button type="submit" id="btnSave" name="Command" value="Save" style="background-color: #3d3c4c;border-radius: 8px;color: white;padding: 5px;border: 1px solid #3d3c4c;">Save</button>
        </div>
    </div>
}

For which I get the following output, enter image description here

I would want to get the checked values as a model.

I have a HttpPost method like this, but it returns null values.

[HttpPost]
public ActionResult AddingRoleSecurity(ViewModel_Role_Security model)
{
    return View();
}

Please let me know how do I get the checked in values in the model?

Thank you very much!

Midwinter answered 18/12, 2014 at 9:40 Comment(11)
How can checkbox value be of string type? A checkbox value should always be either true or false. Why do you want to bind a string type to checkbox?Placable
In AddingRoleSecurity action method you have mentioned the model as ViewModel_Role_Security and in your view you mentioned the model name as ViewModel_User_Role_Security_Page those tow were not matching which each other it is the problem i thinkOften
Hi, I want to get the selected IdS from the selection. How would I set the value from model when the model doesnt have any booleanMidwinter
@Often thanks, that was a typo.. thats not the problemMidwinter
Use view models for Roles and Securities that includes a property bool IsSelected so you can correctly use a strongly typed helpers in collectionsIdleman
I'd generally go for lambda expressions in Html Helper methods. I.e. @Html.CheckBoxFor(m => m.oRoleMaster[i].RoleID.selec) Then it'll figure out the (important) correct name for the checkbox (which if you look at the rendered HTML should be oRoleMaster[1].RoleID.selec )Picayune
@JamesS How do i set RoleID.selec? Also I want to get the selected IDs on clicking save [HttpPost]...?Midwinter
@Midwinter I assumed that was a bool property on the Model. In general you would expect a Model for a checkboxListItem to include an ID property, and a bool property. You need to include a @HiddenFor(m => m.oRoleMaster[i].RoleID.ID) next to every checkboxfor to get the ID posted backPicayune
@JamesS Oh! okay, I get that. My tables dont have a bool/bit value. Can't I do the same with the existing table format? I mean without BooleansMidwinter
@indiaxxo, Just use view models that include bool IsSelected properties so you can do this properly. It would take you far less time than writing this question and you would not have any problems.Idleman
@StephenMuecke All the models are from Entity framework, which are tables from DB. I have added a single bool IsSelected to my ViewModel and got that value.Midwinter
P
13

Just to flesh out my comment above...

For a checkboxList - the viewmodel should include both an identifier property, and boolean selected property. If it doesn't already then either extend or create a new ViewModel class to fulfil this purpose - map your existing model to this specific viewmodel.

i.e. - Your Model Class(es)

public class UserRole
{
  public int RoleID {get; set;}
  public string RoleName {get; set;}
  public bool Selected {get; set;}
}

public class UserSecurity
{
  public int SecurityID {get; set;}
  public string SecurityName {get; set;}
  public bool Selected {get; set;}
}

public class UserRoleAndSecurityModel
{
  public List<UserRole> RoleMaster {get; set;}
  public List<UserSecurity> SecurityMaster {get; set;}
}

Your View: Note that in addition to the checkboxes Html.HiddenFor()has been included for each of the UserRole/UserSecurity ID properties, which allows MVC to bind the ID properties after postback.

@model UserRoleAndSecurityModel   

@using (Html.BeginForm())
{
    <div class="contentsecurity">  
        <div class="User_role">
            <p class="Security_role">User Role</p>

            @for (int i = 0; i < Model.RoleMaster.Count; i++)
            {
                @Html.CheckBoxFor(m => m.RoleMaster[i].Selected)
                @Html.HiddenFor(m => m.RoleMaster[i].RoleId)
                @Html.LabelFor(m => m.RoleMaster[i].Selected, 
                                    Model.RoleMaster[i].RoleName)
                <br />
            }
        </div>

        <div class="User_Page">
            <p class="Security_role">Role Security</p>

            @for (int i = 0; i < Model.SecurityMaster.Count; i++)
            {
                @Html.CheckBoxFor(m => m.SecurityMaster[i].Selected)
                @Html.HiddenFor(m => m.SecurityMaster[i].SecurityId) 
                @Html.LabelFor(m => m.SecurityMaster[i].Selected, 
                                    Model.SecurityMaster[i].SecurityName)
                <br />

            }
        </div>
        <div class="bottombuttonsecurity">
            <button type="submit" id="btnSave" name="Command" value="Save" style="background-color: #3d3c4c;border-radius: 8px;color: white;padding: 5px;border: 1px solid #3d3c4c;">Save</button>
        </div>
    </div>

and your controller should now use the new model above too!

Picayune answered 18/12, 2014 at 13:8 Comment(2)
Thanks a lot for taking time, my problem is Roles and Securities are the entity models. I shouldnt change my table in order to make these changed. So my question was that. Im sorry if I mislead anywhere.. Thanks again!Midwinter
You shouldn't always expect to be able to bind entity framework classes directly to Views - MVC Models should contain properties that have the information to be exchanged with the View. Its perfectly acceptable to create Models just for use in a single view, and have your controller create a new instance and then map the required properties from the entity framework modelPicayune
A
1

I don't want to appear like I'm taking credit for James excellent work, but a new answer is all I can do. For reasons I do not understand, my edit that corrected compile errors in the code were rejected as not correcting critical issues. So I post this as a complete working answer.

The Models:

public class UserRole
{
  public int RoleID {get; set;}
  public string RoleName {get; set;}
  public bool Selected {get; set;}
}

public class UserSecurity
{
  public int SecurityID {get; set;}
  public string SecurityName {get; set;}
  public bool Selected {get; set;}
}

public class UserRoleAndSecurityModel
{
  public List<UserRole> RoleMaster {get; set;}
  public List<UserSecurity> SecurityMaster {get; set;}
}

And the view:

@model UserRoleAndSecurityModel   

@using (Html.BeginForm())
{
    <div class="contentsecurity">  
        <div class="User_role">
            <p class="Security_role">User Role</p>

            @for (int i = 0; i < Model.RoleMaster.Count; i++)
            {
                @Html.CheckBoxFor(m => m.RoleMaster[i].Selected)
                @Html.HiddenFor(m => m.RoleMaster[i].RoleId)
                @Html.LabelFor(m => m.RoleMaster[i].Selected, 
                                    Model.RoleMaster[i].RoleName)
                <br />
            }
        </div>

        <div class="User_Page">
            <p class="Security_role">Role Security</p>

            @for (int i = 0; i < Model.SecurityMaster.Count; i++)
            {
                @Html.CheckBoxFor(m => m.SecurityMaster[i].Selected)
                @Html.HiddenFor(m => m.SecurityMaster[i].SecurityId) 
                @Html.LabelFor(m => m.SecurityMaster[i].Selected, 
                                    Model.SecurityMaster[i].SecurityName)
                <br />

            }
        </div> 

        <div class="bottombuttonsecurity">
            <button type="submit" id="btnSave" name="Command" value="Save" style="background-color: #3d3c4c;border-radius: 8px;color: white;padding: 5px;border: 1px solid #3d3c4c;">Save</button>
        </div>
    </div>
Adamec answered 27/4, 2018 at 8:21 Comment(0)
M
0

I would use https://www.nuget.org/packages/BeginCollectionItem/

This will help you get the right sequence in binding using GUID

Best Regard Burim

Mathian answered 18/12, 2014 at 10:10 Comment(0)
F
0

you should add a third bool property to both of your models,

  public bool IsSelected { get; set; }

and set it defaulted to false, and will get it true on posting back when you check the corresponding check box, and depending upon true values you can do what ever you want with corresponding selected values and texts as well

here is my code which i have used some where to acheive the same:

                    <td colspan="4" style="-webkit-column-count: 3; -moz-column-count:3; -ms-column-count:3; -o-column-count: 3; column-count: 3">
                        @{int i = 0;
                        }
                        @foreach (var item in Model.SurfaceTypeList)
                        {

                            <input name="SurfaceTypeList[@i].Text" type="hidden" value="@item.Text" />
                            <input name="SurfaceTypeList[@i].Value" type="hidden" value="@item.Value" />
                            <input data-val="true" id="SurfaceTypeList_@(i)__IsSelected" name="SurfaceTypeList[@i].IsSelected" type="checkbox" class="SurfaceType" value="true" checked="@item.IsSelected" />
                            @Html.Label(item.Text)
                            <br /><br />
                            i++;
                        }


                    </td>
                    <td colspan="2"></td>

model contains a property:

public List<SurfaceType> SurfaceTypeList { get; set; }
public class SurfaceType
{
public bool IsSelected { get; set; }
    public string Text { get; set; }
    public string Value { get; set; }
}

and here is how i retrieved the text/values:

  foreach (var item in modelData.SurfaceTypeList)
            {
                if (item.IsSelected)
                {
                    var surfaceType = new XElement("SurfaceType");
                    var id = new XElement("Id");
                    id.Add(item.Value);
                    surfaceType.Add(id);
                    var i = id.Value;
                    surfaceTypeList.Add(surfaceType);
                }

            }
Fullback answered 18/12, 2014 at 10:53 Comment(2)
When I return the same model, I just get to see the boolean value selected[true/false]. How do Iget the corresponding ID values?Midwinter
inspect html in google chrome to check for what type of ids you are gettingFullback

© 2022 - 2024 — McMap. All rights reserved.