¿Cómo manejar las casillas de verificación en los formularios ASP.NET MVC?


246

Precaución: ¡Esta pregunta tiene más de nueve años!

Su mejor opción es buscar nuevas preguntas, o buscar las respuestas a continuación buscando su versión específica de MVC, ya que muchas respuestas aquí son obsoletas ahora.

Si encuentra una respuesta que funcione para su versión, asegúrese de que la respuesta contenga la versión de MVC que está utilizando.
(La pregunta original comienza a continuación)


Esto me parece un poco extraño, pero por lo que puedo decir, así es como lo haces.

Tengo una colección de objetos y quiero que los usuarios seleccionen uno o más de ellos. Esto me dice "formulario con casillas de verificación". Mis objetos no tienen ningún concepto de "seleccionado" (son POCO rudimentarios formados al deserializar una llamada wcf). Entonces, hago lo siguiente:

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

En la vista:

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

Y, en el controlador, esta es la única forma en que puedo ver para averiguar qué objetos revisó el usuario:

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

Es extraño en primer lugar, y en segundo lugar, para aquellos elementos que el usuario verificó, FormCollection enumera su valor como "verdadero falso" en lugar de solo verdadero.

Obviamente, me falta algo. Creo que esto se construye con la idea en mente de que los objetos de la colección sobre los que se actúa dentro del formulario html se actualizan a UpdateModel()través de ModelBinder.

Pero mis objetos no están preparados para esto; ¿eso significa que esta es la única manera? ¿Hay otra forma de hacerlo?


2
Otros pueden encontrar esta solución útil: stackoverflow.com/questions/3291501/…
Aaron

Respuestas:


262

Html.CheckBox está haciendo algo extraño: si ve el código fuente en la página resultante, verá que hay un <input type="hidden" /> se genera una junto a cada casilla de verificación, lo que explica los valores "verdaderos falsos" que está viendo para cada elemento del formulario.

Pruebe esto, que definitivamente funciona en ASP.NET MVC Beta porque lo acabo de probar.

Ponga esto en la vista en lugar de usar 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" />
<%}%>

Todas sus casillas de verificación se llaman selectedObjectsy elvalue de cada casilla de verificación es el GUID del objeto correspondiente.

Luego publique en la siguiente acción del controlador (o algo similar que haga algo útil en lugar de Response.Write ())

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

Este ejemplo solo escribirá los GUID de las casillas que marcó; ASP.NET MVC asigna los valores GUID de las casillas de verificación seleccionadas en el Guid[] selectedObjectsparámetro para usted, e incluso analiza las cadenas de la colección Request.Form en objetos GUID instantáneos, lo que creo que es bastante agradable.


¡Sip! ¡Esto es lo que también he tenido que hacer para mis aplicaciones!
Adhip Gupta

70
wtf. campos de entrada ocultos con el MISMO nombre como control. es ViewState 2.0!
Simon_Weaver

3
Esto también está presente en la versión 1.0. Mire la respuesta que envió @ andrea-balducci que le brinda una forma inteligente de lidiar con esto. Si la casilla de verificación no está marcada, el texto resultante recuperado debería ser 'falso falso' - es una buena solución para tener en cuenta los navegadores con muerte cerebral ...
Bryan Rehbein

77
¿Sorpresa? Wtf? La entrada oculta tiene el mismo nombre que la casilla de verificación: si la casilla de verificación con el mismo nombre no está marcada, su valor no se publica, mientras que el valor de lo oculto se publica. La primera vez que el navegador encuentra un elemento con nombre, usará ese valor e ignorará todos los demás elementos con el mismo nombre. Esto garantiza que se envíe un valor: verdadero si la casilla de verificación está marcada (suponiendo que se encuentre por encima del elemento oculto) y falso si la casilla de verificación no está marcada (la casilla de verificación vacía se ignora y lo oculto se convierte en el retroceso). El verdadero problema es por qué nadie más señaló esto.
nerraga

19
@nerraga: Te equivocas: es html válido tener múltiples elementos de formulario con el mismo nombre; y si es así, todos los elementos se publican, y no estoy seguro de que el orden esté realmente definido (aunque es probable que esté simplemente en el orden de las páginas en la práctica). Usar la palabra "falso" como valor es algo engañoso, así que sí, es un WTF: una opción mejor y menos engañosa hubiera sido algo como "existe" o alguna ficha sin sentido como un guión.
Eamon Nerbonne

95

HtmlHelper agrega una entrada oculta para notificar al controlador sobre el estado No verificado. Para tener el estado correcto verificado:

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

1
Este es el enfoque más fácil para este problema y es lo que siempre uso. Le permite utilizar los métodos auxiliares y tener en cuenta el comportamiento de ASP.NET MVC.
Nick Daniels

8
.. o en el caso de no marcado: bool bChecked = form [key] .Equals ("false"); Hacer un .Contains ("falso") falla ya que el valor verdadero es "verdadero, falso".
Mark Robinson el

54

En caso de que se pregunte POR QUÉ ponen un campo oculto con el mismo nombre que la casilla de verificación, el motivo es el siguiente:

Comentario del código fuente MVCBetaSource \ MVC \ src \ MvcFutures \ Mvc \ ButtonsAndLinkExtensions.cs

Renderice un adicional <input type="hidden".../>para casillas de verificación. Esto aborda escenarios en los que no se envían casillas de verificación sin marcar en la solicitud. El envío de una entrada oculta permite saber que la casilla de verificación estaba presente en la página cuando se envió la solicitud.

Supongo que detrás de escena necesitan saber esto para vincular los parámetros en los métodos de acción del controlador. Entonces podría tener un booleano de tres estados, supongo (vinculado a un parámetro bool anulable). No lo he probado, pero espero que eso sea lo que hicieron.


44
Sí, esto es útil en escenarios en los que tiene paginas cuadrículas, etc. y desea anular la selección del elemento que se seleccionó previamente en algún objeto comercial.
RichardOD

49

También debe usarlo <label for="checkbox1">Checkbox 1</label>porque las personas pueden hacer clic en el texto de la etiqueta y en la casilla de verificación. También es más fácil de diseñar y, al menos, en IE, se resaltará cuando desplace los controles de la página.

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

Esto no es solo una cosa de 'oh, podría hacerlo'. Es una mejora significativa de la experiencia del usuario. Incluso si no todos los usuarios saben que pueden hacer clic en la etiqueta, muchos lo harán.


27

Me sorprende que ninguna de estas respuestas haya utilizado las funciones MVC incorporadas para esto.

Escribí una publicación de blog sobre esto aquí , que incluso vincula las etiquetas a la casilla de verificación. Usé el EditorTemplate carpeta para lograr esto de una manera limpia y modular.

Simplemente terminará con un nuevo archivo en la carpeta EditorTemplate que se ve así:

@model SampleObject

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

en su vista real, no habrá necesidad de repetir esto, simplemente 1 línea de código:

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

Visita mi blog para más detalles.


1
¡Gracias! Everboyd todavía usa las formas antiguas. Y cuando envía puede acceder al Modelo sin toda esta colección [] y request.form basura - Esto es lo que estaba buscando. +1y + cerveza
Piotr Kula

2
Esta debería ser la mejor respuesta para la pregunta. Muy simple y fácil de implementar.
Johan Gunawan

Luché por algún tiempo para encontrar una solución elegante a esto antes de encontrar esta respuesta. ¡Tiene unos pocos años pero sigue siendo perfectamente válido en 2016! Si se usa en varios casos, use el SelectListItem incorporado como un tipo genérico que se adapta perfectamente a esto
Phil

¿SampleObject es una lista? Tales como: @ModelType List (Of Type)
JoshYates1980

25

Esto es lo que he estado haciendo.

Ver:


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

Controlador:


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

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

Encontré que los métodos de ayuda Html. * No son tan útiles en algunos casos, y que era mejor hacerlo en HTML antiguo. Siendo uno de ellos, el otro que me viene a la mente son los botones de radio.

Editar: esto está en la Vista previa 5, obviamente YMMV entre versiones.


1
Solo quiero agregar un ejemplo alternativo: Object.Property =! String.IsNullOrEmpty (Request.Form ["NAME"]);
Gup3rSuR4c 05 de

si no está marcada, entonces va a la excepción
Xulfee

10

Parecen optar por leer solo el primer valor, por lo que esto es "verdadero" cuando la casilla de verificación está marcada y "falso" cuando solo se incluye el valor oculto. Esto se obtiene fácilmente con un código como este:

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

8

@Dylan Beattie Gran descubrimiento !!! Muchas gracias Para ampliar aún más, esta técnica también funciona perfectamente con el enfoque Ver modelo. MVC es tan genial que es lo suficientemente inteligente como para vincular una matriz de Guías a una propiedad con el mismo nombre del objeto Modelo vinculado a la Vista. Ejemplo:

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; }
    }
}

Ver:

<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" />
<%}%>                    

Controlador:

    [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();
    }

Cualquier persona familiarizada con la filosofía de Ver modelo se alegrará, ¡esto funciona como un campeón!


Gracias, su SampleViewModel despejó cierta confusión en la respuesta original.
Parlamento

6

También me gustaría señalar que puede nombrar cada casilla de verificación con un nombre diferente y que ese nombre forme parte de los parámetros de los resultados de la acción.

Ejemplo,

Ver:

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

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

Controlador:

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

Los valores de la vista se pasan a la acción ya que los nombres son los mismos.

Sé que esta solución no es ideal para su proyecto, pero pensé en lanzar la idea.


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

Del controlador:

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

         var s = fc["checkbox1"];

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

4

Este problema también ocurre en la versión 1.0. Html.Checkbox () hace que se agregue otro campo oculto con el mismo nombre / id que su casilla de verificación original. Y mientras intentaba cargar una matriz de casillas de verificación usando document.GetElemtentsByName (), puedes adivinar cómo se complicaron las cosas. Es extraño


4

Por lo que puedo deducir, el modelo no quiere adivinar si está marcado = verdadero o falso, solucioné esto estableciendo un atributo de valor en el elemento de casilla de verificación con jQuery antes de enviar el formulario de esta manera:

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

De esta manera, no necesita un elemento oculto solo para almacenar el valor de la casilla de verificación.


4

Sé que esta pregunta se escribió cuando MVC3 no estaba disponible, pero para cualquiera que haga esta pregunta y esté usando MVC3, es posible que desee la forma "correcta" de hacerlo.

Mientras pienso que haciendo todo

Contains("true");

La cosa es genial y limpia, y funciona en todas las versiones de MVC, el problema es que no tiene en cuenta la cultura (como si realmente importara en el caso de un bool).

La forma "correcta" de calcular el valor de un bool, al menos en MVC3, es usar ValueProvider.

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

Hago esto en uno de los sitios de mi cliente cuando edito permisos:

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));
}

Ahora, la belleza de esto es que puede usar esto con casi cualquier tipo simple, e incluso será correcto según la Cultura (piense en dinero, decimales, etc.).

ValueProvider es lo que se utiliza cuando forma sus acciones de esta manera:

public ActionResult UpdatePermissions(bool permission_1, bool permission_2)

pero cuando intenta crear dinámicamente estas listas y verificar los valores, nunca sabrá el Id en el momento de la compilación, por lo que debe procesarlos sobre la marcha.


¿Hay alguna razón por la que usa Request.Params en lugar de agregar un parámetro FormCollection al método?
SwissCoder

No hay razón, realmente no veo ningún beneficio de una manera sobre la otra.
Dan VanWinkle

1
Lo siento, en realidad hay una razón. Solo miré mi código, y estoy usando este método para 5 llamadas diferentes, y no tenía ganas de pasar FormCollection de cada método. Esta era la forma más fácil de obtener las llaves sin tener que pasar nada.
Dan VanWinkle

Sí, soy nuevo en MVC. Y leí en alguna parte que es mejor en general usar FormCollection en lugar de Request.Params, al igual que usar un Modelo escrito en lugar de FormCollection (por lo que en realidad sería mejor usar un modelo y evitar usar Request.Params en general). Noté que otras cosas del código no se usan, como la variable allPermissions y keyAsInt. Gracias por responder.
SwissCoder

3

La forma más fácil de hacerlo es ...

Usted establece el nombre y el valor.

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

Luego, al enviar, tome los valores de las casillas de verificación y guárdelos en una matriz int. entonces la función LinQ apropiada. Eso es..

[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

Igual que la respuesta de nautic20, simplemente use la lista de casillas de enlace de modelo predeterminado MVC con el mismo nombre que una propiedad de colección de string / int / enum en ViewModel. Eso es.

Pero hay que señalar un problema. En cada componente de la casilla de verificación, no debe poner "Id", ya que afectará el enlace del modelo MVC.

El siguiente código funcionará para el enlace del modelo:

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

Los siguientes códigos no se vincularán con el modelo (la diferencia aquí es la identificación asignada para cada casilla de verificación)

<% 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>
<% } %>

Gracias por responder, pero esta pregunta es juuuust un poco viejo. Seis años, aproximadamente. Es posible que desee editar y especificar qué versión de MVC está utilizando. Eso ayudará a cualquiera que use una versión más nueva a encontrar esta respuesta.

Secundo que. La eliminación de la identificación única resolvió este problema después de muchos dolores de cabeza y crujir de dientes
Ody

La versión de MVC que estoy usando es .net MVC 4.0 para este problema.
ChinaHelloWorld

2

Esto es lo que hice para perder los valores dobles al usar el Html.CheckBox (...

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

con 4 casillas marcadas, sin marcar, sin marcar, marcadas se convierte en verdadero, falso, falso, falso, verdadero, falso en verdadero, falso, falso, verdadero. justo lo que necesitaba


0

¿Qué tal algo como esto?

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

0

Cuando uso la casilla de verificación HtmlHelper, prefiero trabajar con los datos del formulario de casilla de verificación publicados como una matriz. Realmente no sé por qué, sé que los otros métodos funcionan, pero creo que prefiero tratar las cadenas separadas por comas como una matriz tanto como sea posible.

Entonces, hacer una prueba 'marcada' o verdadera sería:

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

Hacer una verificación falsa sería:

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

La principal diferencia es usarlo GetValuesya que esto regresa como una matriz.


0

Solo haz esto en $(document).ready:

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

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

    }
});

2
Por desgracia, es posible obtener resultados extraños en el lado del servidor para las personas con JavaScript desactivado (NoScript plug-in, los teléfonos móviles viejos, lince ...)
Arick

0

Mi solución es:

<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>  

Más puede encontrar aquí: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was-not-recognized-as-a- válido-booleano /


No necesita usar ningún Javascript para esto ... var isChecked = formData [key] .Contains ("true");
Jammer

0

Tuve casi el mismo problema, pero el valor de retorno de mi controlador se bloqueó con otros valores.

Encontré una solución simple pero parece un poco difícil.

Intenta escribir Viewbag.tu controlador y ahora le das un nombre comoViewbag.Checkbool

Ahora cambie a la Vista e intente esto @Viewbag.Checkboolcon esto obtendrá el valor del Controlador.

Los parámetros de mi controlador se ven así:

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

y mi casilla de verificación se actualizará así:

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

0

Usando @mmacaulay, se me ocurrió esto para bool:

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

Si está marcado activo = verdadero

Si no está activado activo = falso

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.