web-dev-qa-db-fra.com

Comment gérer les cases à cocher dans les formulaires ASP.NET MVC?

Attention: cette question a plus de neuf ans!

Votre meilleure option est de rechercher de nouvelles questions, ou parmi les réponses ci-dessous, à la recherche de votre version spécifique de MVC, car de nombreuses réponses ici sont obsolètes. 

Si vous trouvez une réponse qui convient à votre version, assurez-vous qu'elle contient bien la version de MVC que vous utilisez.
(La question initiale commence ci-dessous)


Cela me semble un peu bizarre, mais pour autant que je sache, voici comment vous le faites.

J'ai une collection d'objets et je veux que les utilisateurs en sélectionnent un ou plusieurs. Cela me dit "forme avec des cases à cocher". Mes objets n'ont aucun concept de "sélectionné" (ce sont des POCO rudimentaires formés en désérialisant un appel wcf). Donc, je fais ce qui suit:

public class SampleObject{
  public Guid Id {get;set;}
  public string Name {get;set;}
}

Dans la vue:

<%
    using (Html.BeginForm())
    {
%>
  <%foreach (var o in ViewData.Model) {%>
    <%=Html.CheckBox(o.Id)%>&nbsp;<%= o.Name %>
  <%}%>
  <input type="submit" value="Submit" />
<%}%>

Et, dans le contrôleur, c’est le seul moyen dont je dispose pour déterminer les objets vérifiés par l’utilisateur:

public ActionResult ThisLooksWeird(FormCollection result)
{
  var winnars = from x in result.AllKeys
          where result[x] != "false"
          select x;
  // yadda
}

C'est bizarre en premier lieu, et deuxièmement, pour les éléments que l'utilisateur a cochés, FormCollection affiche sa valeur comme "true false" plutôt que simplement true. 

De toute évidence, il me manque quelque chose. Je pense que ceci est construit avec l'idée en tête que les objets de la collection qui sont traités dans le formulaire html sont mis à jour en utilisant UpdateModel() ou par le biais d'un ModelBinder. 

Mais mes objets ne sont pas configurés pour cela; cela signifie-t-il que c'est le seul moyen? Y a-t-il une autre façon de le faire?

246
Will

Html.CheckBox fait quelque chose de bizarre - si vous affichez le source sur la page résultante, vous verrez qu'un <input type="hidden" /> est généré à côté de chaque case à cocher, ce qui explique les valeurs "true false" que vous voyez pour chaque élément de formulaire.

Essayez ceci, qui fonctionne vraiment sur ASP.NET MVC Beta parce que je viens de l'essayer.

Mettez ceci dans la vue au lieu d'utiliser Html.CheckBox ():

<% using (Html.BeginForm("ShowData", "Home")) {  %>
  <% foreach (var o in ViewData.Model) { %>
    <input type="checkbox" name="selectedObjects" value="<%=o.Id%>">
    <%= o.Name %>
  <%}%>
  <input type="submit" value="Submit" />
<%}%>

Vos cases à cocher sont toutes appelées selectedObjects et la value de chaque case est le GUID de l'objet correspondant.

Ensuite, publiez l'action suivante du contrôleur (ou quelque chose de similaire qui fait quelque chose d'utile au lieu de Response.Write ())

public ActionResult ShowData(Guid[] selectedObjects) {
    foreach (Guid guid in selectedObjects) {
        Response.Write(guid.ToString());
    }
    Response.End();
    return (new EmptyResult());
}

Cet exemple va simplement écrire les GUID des cases que vous avez cochées; ASP.NET MVC mappe les valeurs GUID des cases à cocher sélectionnées dans le paramètre Guid[] selectedObjects et analyse même les chaînes de la collection Request.Form dans des objets instanciés GUID, ce qui me semble plutôt agréable.

262
Dylan Beattie

HtmlHelper ajoute une entrée cachée pour informer le contrôleur de l'état non vérifié . Donc, pour avoir le statut vérifié correct:

bool bChecked = form[key].Contains("true");
95
Andrea Balducci

Dans le cas où vous vous demandez POURQUOI ils ont mis un champ caché avec le même nom que la case à cocher, la raison est la suivante:

Commentaire du code source MVCBetaSource\MVC\src\MvcFutures\Mvc \BoutonsAndLinkExtensions.cs

Rendre un <input type="hidden".../> supplémentaire pour les cases à cocher . Cela concerne des scénarios où les cases non cochées ne sont pas envoyées la demande. Envoi d'une entrée masquée permet de savoir que le case à cocher était présent sur la page quand la demande a été soumise.

Je suppose qu’en arrière-plan, ils doivent le savoir pour pouvoir se lier aux paramètres des méthodes d’action du contrôleur. Vous pourriez alors avoir un booléen à trois états, je suppose (lié à un paramètre bool nullable). Je n'ai pas essayé mais j'espère que c'est ce qu'ils ont fait.

54
Simon_Weaver

Vous devez également utiliser <label for="checkbox1">Checkbox 1</label> car les utilisateurs peuvent cliquer sur le texte de l'étiquette ainsi que sur la case à cocher elle-même. Il est également plus facile de styler et au moins dans IE, il sera mis en surbrillance lorsque vous parcourez les contrôles de la page.

<%= Html.CheckBox("cbNewColors", true) %><label for="cbNewColors">New colors</label>

Ce n'est pas juste une chose 'oh je pourrais le faire'. C'est une amélioration significative de l'expérience utilisateur. Même si tous les utilisateurs ne savent pas qu'ils peuvent cliquer sur l'étiquette, beaucoup le feront. 

49
Simon_Weaver

Je suis surpris qu'aucune de ces réponses n'utilise les fonctionnalités intégrées de MVC pour cela.

J'ai écrit un article sur ce blog ici , qui lie même les étiquettes à la case à cocher. J'ai utilisé le dossier EditorTemplate pour y parvenir de manière propre et modulaire.

Vous allez simplement vous retrouver avec un nouveau fichier dans le dossier EditorTemplate qui ressemble à ceci:

@model SampleObject

@Html.CheckBoxFor(m => m.IsChecked)
@Html.HiddenFor(m => m.Id)
@Html.LabelFor(m => m.IsChecked, Model.Id)

dans votre vue actuelle, il n'y aura pas besoin de boucler ceci, simplement 1 ligne de code:

@Html.EditorFor(x => ViewData.Model)

Visitez mon blog post pour plus de détails.

27
Shawn Mclean

Voici ce que j'ai fait.

Vue:


<input type="checkbox" name="applyChanges" />

Manette:


var checkBox = Request.Form["applyChanges"];

if (checkBox == "on")
{
...
}

J'ai trouvé les méthodes d'assistance Html. * Peu utiles dans certains cas, et il valait mieux que je le fasse avec du vieux HTML ordinaire. Ceci étant l’un d’eux, l’autre qui me vient à l’esprit est le bouton radio.

Edit: c'est sur Preview 5, évidemment YMMV entre les versions.

25
mmacaulay

Ils semblent choisir de ne lire que la première valeur. C'est donc "vrai" lorsque la case à cocher est cochée et "faux" lorsque seule la valeur cachée est incluse. Ceci est facilement récupéré avec un code comme celui-ci:

model.Property = collection["ElementId"].ToLower().StartsWith("true");
10
Fluffy

@Dylan Beattie Excellente découverte !!! Je vous remercie beaucoup. Pour étendre encore plus loin, cette technique fonctionne également parfaitement avec l'approche View Model. MVC est tellement cool qu'il est assez intelligent pour lier un tableau de Guids à une propriété du même nom que l'objet Model lié à la vue. Exemple:

ViewModel:

public class SampleViewModel
{
    public IList<SampleObject> SampleObjectList { get; set; }
    public Guid[] SelectedObjectIds { get; set; }

    public class SampleObject
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
}

Vue:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Sample View</h2>
<table>
    <thead> 
        <tr>
            <th>Checked</th>
            <th>Object Name</th>
        </tr>
    </thead> 
<% using (Html.BeginForm()) %>
<%{%>                    
    <tbody>
        <% foreach (var item in Model.SampleObjectList)
           { %>
            <tr>
                <td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
                <td><%= Html.Encode(item.Name)%></td>
            </tr>
        <% } %>
    </tbody>
</table>
<input type="submit" value="Submit" />
<%}%>                    

Manette:

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult SampleView(Guid id)
    {
        //Object to pass any input objects to the View Model Builder 
        BuilderIO viewModelBuilderInput = new BuilderIO();

        //The View Model Builder is a conglomerate of repositories and methods used to Construct a View Model out of Business Objects
        SampleViewModel viewModel = sampleViewModelBuilder.Build(viewModelBuilderInput);

        return View("SampleView", viewModel);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SampleView(SampleViewModel viewModel)
    {
        // The array of Guids successfully bound to the SelectedObjectIds property of the View Model!
        return View();
    }

Toute personne familiarisée avec la philosophie de View Model se réjouira, cela fonctionne comme un champion!

8
nautic20

Je voudrais également souligner que vous pouvez nommer chaque case à cocher d'un nom différent, et que ce nom fasse partie des paramètres d'actionresults.

Exemple,

Vue: 

 <%= Html.CheckBox("Rs232CheckBox", false, new { @id = "rs232" })%>RS-232

 <%= Html.CheckBox("Rs422CheckBox", false, new { @id = "rs422" })%>RS-422

Manette:

public ActionResults MyAction(bool Rs232CheckBox, bool Rs422CheckBox) {
    ...
}

Les valeurs de la vue sont transmises à l'action car les noms sont les mêmes.

Je sais que cette solution n'est pas idéale pour votre projet, mais je pensais que l'idée serait lancée.

6
Darcy
<input type = "checkbox" name = "checkbox1" /> <label> Check to say hi.</label>

Du contrôleur:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Index(FormCollection fc)
    {

         var s = fc["checkbox1"];

         if (s == "on")
         {
             string x = "Hi";
         }
    }
5
bluwater2001

Je sais que cette question a été écrite à l’époque où MVC3 n’était pas disponible, mais pour tous ceux qui viennent à cette question et utilisent MVC3, vous voudrez peut-être la "bonne" façon de le faire.

Bien que je pense que faire le tout

Contains("true");

la chose est géniale et propre, et fonctionne sur toutes les versions de MVC, le problème est qu’elle ne tient pas compte de la culture (comme si c’était vraiment important dans le cas d’un bool).

La façon "correcte" de déterminer la valeur d'une valeur booléenne, du moins dans MVC3, consiste à utiliser ValueProvider.

var value = (bool)ValueProvider.GetValue("key").ConvertTo(typeof(bool));

Je le fais dans l'un des sites de mes clients lorsque je modifie des autorisations:

var allPermissionsBase = Request.Params.AllKeys.Where(x => x.Contains("permission_")).ToList();
var allPermissions = new List<KeyValuePair<int, bool>>();

foreach (var key in allPermissionsBase)
{
     // Try to parse the key as int
     int keyAsInt;
     int.TryParse(key.Replace("permission_", ""), out keyAsInt);

     // Try to get the value as bool
     var value = (bool)ValueProvider.GetValue(key).ConvertTo(typeof(bool));
}

Maintenant, la beauté de ceci est que vous pouvez l'utiliser avec n'importe quel type simple, et ce sera même correct en fonction de la culture (pensez à l'argent, aux décimales, etc.).

ValueProvider est ce qui est utilisé lorsque vous formez vos actions comme ceci:

public ActionResult UpdatePermissions(bool permission_1, bool permission_2)

mais lorsque vous essayez de construire ces listes de manière dynamique et de vérifier les valeurs, vous ne saurez jamais l'identifiant au moment de la compilation, vous devez donc les traiter à la volée.

4
Dan VanWinkle

Ce problème se produit également dans la version 1.0. Html.Checkbox () provoque l'ajout d'un autre champ caché avec le même nom/id que celui de votre case à cocher d'origine. Et pendant que j'essayais de charger un tableau de cases à cocher en utilisant document.GetElemtentsByName (), vous pouvez deviner comment les choses se sont égarées. C'est bizarre.

4
Farhan Zia

D'après ce que je peux comprendre, le modèle ne veut pas deviner s'il est coché = vrai ou faux. J'ai contourné cela en définissant un attribut value sur l'élément checkbox avec jQuery avant de soumettre le formulaire de la manière suivante:

 $('input[type="checkbox"]').each(function () {
       $(this).attr('value', $(this).is(':checked'));
 }); 

Ainsi, vous n'avez pas besoin d'un élément caché pour stocker la valeur de la case à cocher.

4
BraveNewMath

Identique à la réponse de nautic20, utilisez simplement la liste de cases à cocher de liaison de modèle par défaut MVC avec le même nom qu'une propriété de collection string/int/enum dans ViewModel. C'est ça.

Mais un problème doit être souligné. Dans chaque composant de case à cocher, vous ne devez pas y mettre "Id", ce qui affectera la liaison du modèle MVC.

Le code suivant fonctionnera pour la liaison de modèle:

 <% foreach (var item in Model.SampleObjectList)
       { %>
        <tr>
            <td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
            <td><%= Html.Encode(item.Name)%></td>
        </tr>
 <% } %>

Les codes suivants ne seront pas liés au modèle (la différence est son identifiant pour chaque case à cocher)

<% foreach (var item in Model.SampleObjectList)
       { %>
        <tr>
            <td><input type="checkbox" name="SelectedObjectIds" id="[some unique key]" value="<%= item.Id%>" /></td>
            <td><%= Html.Encode(item.Name)%></td>
        </tr>
<% } %>
3
ChinaHelloWorld

La meilleure façon de faire est de.

Vous définissez le nom et la valeur.

<input type="checkbox" name="selectedProducts" value="@item.ProductId" />@item.Name

Ensuite, lors de la soumission, saisissez les valeurs des cases à cocher et enregistrez-les dans un tableau int .. .. puis la fonction LinQ appropriée. C'est tout..

[HttpPost]
        public ActionResult Checkbox(int[] selectedObjects)
        {
            var selected = from x in selectedObjects
                           from y in db
                           where y.ObjectId == x
                           select y;                   

            return View(selected);
        }
3
kk-dev11

c’est ce que j’ai fait pour perdre les valeurs doubles en utilisant Html.CheckBox (...

Replace("true,false","true").Split(',')

avec 4 cases cochées, décochée, décochée, cochée, elle devient vrai, faux, faux, faux, vrai, faux en vrai, faux, faux, vrai. juste ce dont j'avais besoin

2
Jeroen

Ma solution est:

<input type="checkbox"  id="IsNew-checkbox"  checked="checked" />     
<input type="hidden"  id="IsNew" name="IsNew" value="true"  />      
<script language="javascript" type="text/javascript" >     
  $('#IsNew-checkbox').click(function () {    
      if ($('#IsNew-checkbox').is(':checked')) {    
          $('#IsNew').val('true');    
      } else {    
          $('#IsNew').val('false');    
       }    
  });   
</script>  

Plus vous pouvez trouver ici: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was -not-reconnu-comme-un-valide-boolean/

0
pawlom84

En utilisant @mmacaulay, j'ai créé ceci pour bool:

// MVC Work around for checkboxes.
bool active = (Request.Form["active"] == "on");

Si coché Actif = true

Si décochée Active = false

0
Ravi Ram

Lorsque vous utilisez la case à cocher HtmlHelper, je préfère de loin travailler avec les données de formulaire de case à cocher publiées sous forme de tableau. Je ne sais pas vraiment pourquoi, je sais que les autres méthodes fonctionnent, mais je pense que je préfère simplement traiter les chaînes séparées par des virgules autant que possible.

Donc, faire un test "vérifié" ou vrai serait:

//looking for [true],[false]
bool isChecked = form.GetValues(key).Contains("true"); 

Faire un faux chèque serait:

//looking for [false],[false] or [false]
bool isNotChecked = !form.GetValues(key).Contains("true"); 

La principale différence est d'utiliser GetValues car cela retourne sous forme de tableau.

0
eyesnz

J'ai eu presque le même problème, mais la valeur de retour de mon contrôleur a été bloquée avec d'autres valeurs.

Trouvé une solution simple mais cela semble un peu rude.

Essayez de taper Viewbag. dans votre contrôleur et maintenant vous lui donnez un nom comme Viewbag.Checkbool

Passez maintenant à la vue et essayez ceci @Viewbag.Checkbool avec ceci, vous obtiendrez la valeur du contrôleur.

Mes paramètres de contrôleur ressemblent à ceci:

public ActionResult Anzeigen(int productid = 90, bool islive = true)

et ma case à cocher se mettra à jour comme ceci:

<input id="isLive" type="checkbox" checked="@ViewBag.Value" ONCLICK="window.location.href = '/MixCategory/Anzeigen?isLive=' + isLive.checked.toString()" />
0
treborian

Que diriez-vous quelque chose comme ça?

bool isChecked = false;
if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
    ModelState.AddModelError(”chkHuman”, “Nice try.”);
0
Skadoosh

Il suffit de faire ceci sur $(document).ready

$('input:hidden').each(function(el) {
    var that = $(this)[0];
    if(that.id.length < 1 ) {

        console.log(that.id);
        that.parentElement.removeChild(that);

    }
});
0
doronAv