1

Before i start i am not sure if i searched "correctly" i have tried multiple things on what i will explain and none of them works at the end because i have to use html helpers.
what i am trying to do is using ccs3 create custom checkbox. i found multiple designs and i just followed them but as i use Html.CheckBoxFor(..., htmlAttributes: new { id = "A Number"}). "A Number" here is just a generated number that goes threw a loop, just so i don't do things manually.

this is how i handle the custom checkboxes (1)

<p>
            <input type="checkbox" value="..." id="@count" />                
                <label for="@count"><span class="ui"></span>Name</label>
            </p>

when i use html helper checkboxfor and when i use it this way it wont work i am not sure why or i am just confused about it. (2)

<p>
            @Html.CheckBoxFor(m => m..., htmlAttributes: new { id = count })

            <label for="@count"><span class="ui"></span>...</label>
        </p>

now when i tried (2) the first time it didn't work and i thought it was from me but when i tried (1) it worked wonders expect that when i submit it doesn't return the value of the checkbox. when i searched i found this Addind css classes to razor elements the response from darin-dimitrov was interesting but i use a custom template calling it simply by Layout = "..." in my view. this part of the answer is flou for me i am not sure how i would declare this nor how i called and if it's unique or changes all.
@Html.TextBox( "", ViewData.TemplateInfo.FormattedModelValue, ViewData )
css3 code

/*
    Custom Checkboxes
*/

[type="checkbox"]:not(:checked),
    input[type="checkbox"]:checked {
        position: absolute;  
        left: -9999px;
    }

[type="checkbox"]:not(:checked) + label,
    [type="checkbox"]:checked + label {
        position: relative;
        padding-left: 75px;
        cursor: pointer;
    }

    [type="checkbox"]:not(:checked) + label:before,
    [type="checkbox"]:checked + label:before,
    [type="checkbox"]:not(:checked) + label:after,
    [type="checkbox"]:checked + label:after {
        content: '';
        position: absolute;
    }
    [type="checkbox"]:not(:checked) + label:before,
    [type="checkbox"]:checked + label:before {
        left:0; top: -3px;
        width: 65px; height: 30px;
        background: #DDDDDD;
        border-radius: 15px;
        -webkit-transition: background-color .2s;
        -moz-transition: background-color .2s;
        -ms-transition: background-color .2s;
        transition: background-color .2s;
    }
    [type="checkbox"]:not(:checked) + label:after,
    [type="checkbox"]:checked + label:after {
        width: 20px; height: 20px;
        -webkit-transition: all .2s;
        -moz-transition: all .2s;
        -ms-transition: all .2s;
        transition: all .2s;
        border-radius: 50%;
        background: #7F8C9A;
        top: 2px; left: 5px;
    }

    /* on checked */
    [type="checkbox"]:checked + label:before {
        background:#34495E; 
    }
    [type="checkbox"]:checked + label:after {
        background: #39D2B4;
        top: 2px; left: 40px;
    }

    [type="checkbox"]:checked + label .ui,
    [type="checkbox"]:not(:checked) + label .ui:before,
    [type="checkbox"]:checked + label .ui:after {
        position: absolute;
        left: 6px;
        width: 65px;
        border-radius: 15px;
        font-size: 16px;
        font-weight: bold;
        line-height: 22px;
        -webkit-transition: all .2s;
        -moz-transition: all .2s;
        -ms-transition: all .2s;
        transition: all .2s;
    }
    [type="checkbox"]:not(:checked) + label .ui:before {
        content: "non";
        left: 32px
    }
    [type="checkbox"]:checked + label .ui:after {
        content: "oui";
        color: #39D2B4;
    }
[type="checkbox"]:focus + label:before {
    border: 1px dashed #777;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
    margin-top: -1px;

UPDATE
The View
I can provide an explanation if needed of the view

@model WebAppWithEF.Models.MainModel
@using WebAppWithEF.Models;

@{
    ViewBag.Title = "Page Title";
    Layout = "~/Views/Shared/_TemplateRest.cshtml"; 
}

@using (Html.BeginForm(null, "Export", new { entity_name = Model.which_entity_to_show }, FormMethod.Post, null))
{
    <div>
        <span class="text-uppercase">Nom Définition </span>
        @Html.EditorFor(m => m.exportDefinition.name, new { htmlAttributes = new { maxlength = "50" } })  <br />
        @if (Model.is_any_checked == false)
        {
            @Html.Label("Error MSG", htmlAttributes: new { @class = "text-danger" }) <br/>
        }
        <br />
        @for (int count = 0; count < Model.selectedFields.Count(); count++)
        {
            <p>
                @Html.CustomCheckBoxFor(m => m.selectedFields[count].propertyStatus)
                @Html.CustomLabelFor(m => m.selectedFields[count].propertyStatus, Model.selectedFields[count].propertyName, "<span class=\"ui\"></span>", new { @class = "class" })
            </p>
                @Html.HiddenFor(m => m.selectedFields[count].propertyStatus)
                @Html.HiddenFor(m => m.selectedFields[count].propertyName)
        }

        @Html.HiddenFor(m => m.exportDefinition.pkey)
        @Html.HiddenFor(m => m.exportDefinition.entityID)

        <div class="col-md-offset-0 col-md-10">
            <div class="contact-button">
                <button name="SaveDefinition" onclick="submit()">Enregistrer/Modifier</button>
            </div>
        </div>

        <div class="col-md-offset-0 col-md-10">
            <div class="contact-button">
                <button name="SpecificExport" onclick="submit()">Exporter</button>
            </div>
        </div>

        @if (!Model.is_new)
        {
            <div class="col-md-offset-0 col-md-10">
                <div class="contact-button">
                    <button name="DeleteDefinition" onclick="submit()">Supprimer</button>
                </div>
            </div>
        }

        <div class="col-md-offset-0 col-md-10">
            <div class="contact-button">
                <button onclick="@Url.Action("Index", "Home" , new { entity_name = ViewBag.entity_name })">Retour</button>
            </div>
        </div>
    </div>
}
Community
  • 1
  • 1

2 Answers2

0

One of the problems was that @Html.CheckBoxFor(.... generates a hidden input that makes mess with your CSS rules. Second don't add in htmlAttributes an id. Also you need a span tag inside your label.

Additional information: Hidden input checkbox for, Include inner element with HTML helper

Solution: Make two extensions of @Html.LabelFor() and @Html.CheckBoxFor :

UPDATE: checkbox extension no need input type hidden anymore

Checkbox extension implementation

public static class CheckboxExtensitons
{
    public static MvcHtmlString CustomCheckBoxFor<TModel, TValue>(
        this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, TValue>> expression,
        object htmlAttributes = null)
    {
        var expressionText = ExpressionHelper.GetExpressionText(expression);
        var htmlAtributesDictionary = new RouteValueDictionary(htmlAttributes);
        // TagBuilder hidden = new TagBuilder("input");
        // hidden.Attributes.Add("type", "hidden");
        // hidden.Attributes.Add("name", expressionText);
        // hidden.Attributes.Add("value", "false");

        TagBuilder input = new TagBuilder("input");
        input.Attributes.Add("type", "checkbox");
        input.Attributes.Add("name", expressionText);
        input.Attributes.Add("id", expressionText);
        input.Attributes.Add("value", "true"); // update
        input.MergeAttributes(htmlAtributesDictionary);

        MvcHtmlString result = MvcHtmlString.Create(input.ToString());
        return result;
    }
}

Label extension implementation

public static class LabelExtensions
{
    public static MvcHtmlString CustomLabelFor<TModel, TPropery>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TPropery>> expression, 
         string labelValue,
        string innerHtml,
        object htmlAttributes = null)
    {
        TagBuilder tag = new TagBuilder("label");
        var htmlAtributesDictionary = new RouteValueDictionary(htmlAttributes);
        //Add the specified Html attributes
        tag.MergeAttributes(htmlAtributesDictionary);

        var expressionText = ExpressionHelper.GetExpressionText(expression);
        tag.Attributes.Add("for", expressionText);
        tag.InnerHtml = innerHtml + labelValue;
        return MvcHtmlString.Create(tag.ToString());
    }
}

Additional I tested with this model, view and controller action:

Model:

public class TeamChoice
{
    public bool TeamCap { get; set; }

    public bool TeamIronMan { get; set; }
}

Actions:

public ActionResult Team()
{
    var model = new TeamChoice();
    return this.View(model);
}

[HttpPost]
public ActionResult Team(TeamChoice model)
{
    return this.RedirectToAction("Team");
}

View:

@model TeamChoice
@using Namespace.OfExtensions; // Important!
@using (Html.BeginForm("Team","Home", FormMethod.Post, null))
{
    <p>
        @Html.CustomCheckBoxFor(a => a.TeamCap)
        @Html.CustomLabelFor(m => m.TeamCap, "Team Cap", "<span class=\"ui\"></span>", new { @class = "class" })
    </p>
    <p>
        @Html.CustomCheckBoxFor(m => m.TeamIronMan)
        @Html.CustomLabelFor(m => m.TeamIronMan, "Team Iron Man", "<span class=\"ui\"></span>", new { @class = "class" })
    </p>
<input type="submit" />
}
Community
  • 1
  • 1
mihkov
  • 1,171
  • 13
  • 37
  • thank you for the info i will try it this week hopefully. i wanted to ask why should i not use the id on a checkboxfor? the way the css code works is it hides the default checkbox to create the ui ones (if i remember correctly) – I_Tried_Thats_Why_Im_Here May 12 '16 at 18:41
  • " i wanted to ask why should i not use the id on a checkboxfor?" It's automatically generated and added to HTML. By default it's equals to model Property name. `@Html.CustomCheckBoxFor(a => a.TeamCap)` => `` – mihkov May 12 '16 at 23:39
  • so now i made it work thanks to you but when i submit the values of the ckechbox are not sent. is there a reason to it? i am trying to figure it out atm but i see nothing wrong so far. I changed back (just to see) using classic checkboxFor without the css code and it works, same values as the customCheckboxFor. – I_Tried_Thats_Why_Im_Here May 13 '16 at 16:40
  • so now the css is the problem? Does checkbox has a attribute `name` in the generated HTML? – mihkov May 13 '16 at 17:53
  • i actually don't know what the problem is maybe the custom can't submit it's values i can't guess maybe i have to receive the custom ni a different way when submitting (add a param, idk) and to the 2nd question yes. – I_Tried_Thats_Why_Im_Here May 13 '16 at 18:06
  • i commented the css code to see if it causes the issue but it doesn't, the issue still persists. my guess is the custom checkboxes are not submitting maybe i missed something. – I_Tried_Thats_Why_Im_Here May 13 '16 at 18:09
  • The problem was in the checkbox extension, please check my update now. when data is submitted - model binding is correct – mihkov May 13 '16 at 19:10
  • i have made the htmlattributes null as you did in the form and changed the customcheckbox the way you did it but it's not working. is it possible to bind it to a hidden value so it wont be an issue anymore ? i have added a hiddenFor that contains the value of the checkbox so when submitted it would return but it's not working or not binded to the custom checkbox. i am honestly a bit lost at this point. – I_Tried_Thats_Why_Im_Here May 13 '16 at 20:25
  • Did you add this row to your code in checkbox extension: `input.Attributes.Add("value", "true"); ` – mihkov May 13 '16 at 20:53
  • This line fix the bug when you submit form. So can you send me your code to check where is the problem? If you can't edit your post - you can upload your code in pastebin.com and send me a link and I will check what's wrong. – mihkov May 14 '16 at 07:24
  • alright i will update adding the view and i will keep adding at your request xD – I_Tried_Thats_Why_Im_Here May 14 '16 at 14:10
0

I will post another answer with some code here to match your models and view after the update.

After your update, I have some questions. I've changed my code as follows:

public ActionResult Team()
{
    var model = new TeamChoice();
    model.exportDefinition = new exportDefinition()
    {
        entityID = "8831A386-18D6-4EAE-919E-9C1D316CC2DD",
        name = "EntryName",
        pkey = "PKeyu"
    };

    model.is_any_checked = true;
    model.which_entity_to_show = "MineEntry";
    model.is_new = false;
    model.selectedFields = new List<Field>();
    for (int i = 1; i <= 15; i++)
    {
        model.selectedFields.Add(new Field()
        {
            propertyName = "Property" + i,
            propertyStatus = i % 2 == 0 ? false : true   
        });
    }

    return this.View(model);
}

[HttpPost]
public ActionResult Team(TeamChoice model)
{
    // this.Request.Form: 
    // exportDefinition.name=EntryName&selectedFields[0].propertyStatus=true&
    // selectedFields[0].propertyStatus=False&selectedFields[0].propertyName=Property1&
    // selectedFields[1].propertyStatus=true&selectedFields[1].propertyStatus=False&
    // selectedFields[1].propertyName=Property2&selectedFields[2].propertyStatus=true&
    // selectedFields[2].propertyStatus=False&selectedFields[2].propertyName=Property3&
    // selectedFields[3].propertyStatus=False&selectedFields[3].propertyName=Property4& and so on....
    // First tree fields in the collection will have status "true"
    var propertyStatus1 = model.selectedFields[0].propertyStatus;
    var propertyStatus2 = model.selectedFields[1].propertyStatus;
    var propertyStatus3 = model.selectedFields[2].propertyStatus;

    return this.RedirectToAction("Team");
}

When you passing the model to the view do you set "true" in the propertyStatus of any field? And second can you verify when you check first tree checkboxes to "Oui" and submit the form do you have form-data like this below?

selectedFields[0].propertyStatus=true& selectedFields[1].propertyStatus=true& selectedFields[2].propertyStatus=true& selectedFields[3].propertyStatus=false ....

UPDATE Update your checkbox extension to code below. When you pass True in propertyStatus the checkbox in the HTML will be checked.

public static MvcHtmlString CustomCheckBoxFor<TModel>(
        this HtmlHelper<TModel> htmlHelper, 
        Expression<Func<TModel, bool>> expression,
        object htmlAttributes = null)
{
    var expressionText = ExpressionHelper.GetExpressionText(expression);
    var htmlAtributesDictionary = new RouteValueDictionary(htmlAttributes);

     TagBuilder input = new TagBuilder("input");
     input.Attributes.Add("type", "checkbox");
     input.Attributes.Add("name", expressionText);
     input.Attributes.Add("id", expressionText);
     input.Attributes.Add("value", "True");
     input.Attributes.Add("data-val", "True");

     ModelMetadata modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
     bool value;
     if (modelMetadata.Model != null && bool.TryParse(modelMetadata.Model.ToString(), out value))
     {
         if (value)
         {
             input.Attributes.Add("checked", "checked");
         }
     }


     MvcHtmlString result = MvcHtmlString.Create(input.ToString());
     return result;
 }

and replace in your view @Html.HiddenFor(m => m.selectedFields[count].propertyStatus) to @Html.Hidden("selectedFields[" + count + "].propertyStatus", false)

mihkov
  • 1,171
  • 13
  • 37