Come gestire le caselle di controllo nei moduli MVC ASP.NET?


246

Attenzione: questa domanda ha più di nove anni!

L'opzione migliore è cercare domande più recenti o cercare le risposte di seguito alla ricerca della versione specifica di MVC, poiché molte risposte qui sono ormai obsolete.

Se trovi una risposta che funziona per la tua versione, assicurati che la risposta contenga la versione di MVC che stai utilizzando.
(La domanda originale inizia di seguito)


Questo mi sembra un po 'strano, ma per quanto ne so, è così che lo fai.

Ho una raccolta di oggetti e desidero che gli utenti selezionino uno o più di essi. Questo mi dice "modulo con caselle di controllo". I miei oggetti non hanno alcun concetto di "selezionato" (sono rudimentali POCO formati deserializzando una chiamata wcf). Quindi, faccio quanto segue:

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

Nella vista:

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

E, nel controller, questo è l'unico modo che posso vedere per capire quali oggetti ha controllato l'utente:

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

È strano in primo luogo, e in secondo luogo, per quegli elementi controllati dall'utente, FormCollection elenca il suo valore come "vero falso" piuttosto che solo vero.

Ovviamente, mi manca qualcosa. Penso che questo sia costruito pensando all'idea che gli oggetti nella raccolta su cui si agiscono all'interno del modulo html vengono aggiornati utilizzando UpdateModel()o tramite un ModelBinder.

Ma i miei oggetti non sono predisposti per questo; significa che questo è l'unico modo? C'è un altro modo per farlo?


2
Altri potrebbero trovare utile questa soluzione: stackoverflow.com/questions/3291501/…
Aaron,

Risposte:


262

Html.CheckBox sta facendo qualcosa di strano: se visualizzi l'origine nella pagina risultante, vedrai che <input type="hidden" />viene generato un insieme a ciascuna casella di controllo, il che spiega i valori "true false" che stai vedendo per ogni elemento del modulo.

Prova questo, che sicuramente funziona su ASP.NET MVC Beta perché l'ho appena provato.

Metti questo nella vista invece di usare 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" />
<%}%>

Le tue caselle di controllo sono tutte chiamate selectedObjectse la valuecasella di controllo di ciascuna è il GUID dell'oggetto corrispondente.

Quindi pubblica la seguente azione del controller (o qualcosa di simile che fa qualcosa di utile invece di Response.Write ())

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

Questo esempio scriverà semplicemente i GUID delle caselle selezionate; ASP.NET MVC associa i valori GUID delle caselle di controllo selezionate nel Guid[] selectedObjectsparametro per te e analizza persino le stringhe dalla raccolta Request.Form in oggetti GUID istanti, che ritengo piuttosto carini.


Sì! Questo è quello che ho dovuto fare anche per le mie applicazioni!
Adhip Gupta,

70
WTF. campi di input nascosti con il nome SAME come controllo. il suo ViewState 2.0!
Simon_Weaver,

3
Questo è presente anche nella versione 1.0. Guarda la risposta inviata da @ andrea-balducci che ti offre un modo intelligente di gestirlo. Se la casella di controllo non è selezionata, il testo risultante recuperato dovrebbe essere "falso falso" - è una buona soluzione per tenere conto dei browser cerebrali morti ...
Bryan Rehbein,

77
Sorpresa? Wtf? L'input nascosto ha lo stesso nome della casella di controllo: se la casella di controllo con lo stesso nome non è selezionata, il suo valore non viene registrato mentre viene pubblicato il valore di nascosto. La prima volta che il browser incontra un elemento con nome, utilizzerà quel valore e ignorerà tutti gli altri elementi con lo stesso nome. Ciò garantisce l'invio di un valore: vero se la casella di controllo è selezionata (presupponendo che sia presente sopra l'elemento nascosto) e falsa se la casella di controllo è deselezionata (la casella di controllo vuota viene ignorata e quella nascosta diventa il fallback). Il vero wtf è il motivo per cui nessun altro lo ha sottolineato.
nerraga,

19
@nerraga: ti sbagli: è html valido avere più elementi del modulo con lo stesso nome; e in tal caso, tutti gli elementi vengono pubblicati e non sono sicuro che l'ordine sia effettivamente definito (anche se probabilmente sarà semplicemente nell'ordine delle pagine in pratica). Usare la parola "falso" come valore è in qualche modo fuorviante, quindi sì, è un WTF - una scelta migliore, meno fuorviante sarebbe stata qualcosa come "esiste" o un token insignificante come un trattino.
Eamon Nerbonne,

95

HtmlHelper aggiunge un input nascosto per notificare al controller lo stato Non controllato. Quindi per avere lo stato corretto verificato:

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

1
Questo è l'approccio più semplice a questo problema ed è quello che uso sempre. Consente di utilizzare i metodi di supporto e di tenere conto del comportamento degli MVC ASP.NET.
Nick Daniels,

8
.. o nel caso non verificato: bool bChecked = form [chiave] .Equals ("false"); Fare un .Contains ("false") fallisce perché il valore vero è "true, false".
Mark Robinson,

54

Nel caso ti stia chiedendo PERCHÉ hanno inserito un campo nascosto con lo stesso nome della casella di controllo, il motivo è il seguente:

Commento dal codice sorgente MVCBetaSource \ MVC \ src \ MvcFutures \ Mvc \ ButtonsAndLinkExtensions.cs

Rendering di un ulteriore <input type="hidden".../>per le caselle di controllo. Questo risolve gli scenari in cui le caselle di controllo non selezionate non vengono inviate nella richiesta. L'invio di un input nascosto consente di sapere che la casella di controllo era presente sulla pagina al momento dell'invio della richiesta.

Immagino che dietro le quinte abbiano bisogno di sapere questo per legarsi ai parametri sui metodi di azione del controller. Potresti quindi avere un booleano tri-stato suppongo (associato a un parametro bool nullable). Non l'ho provato ma spero che sia quello che hanno fatto.


4
Sì, questo è utile negli scenari in cui sono presenti griglie paginate, ecc. E si desidera deselezionare l'elemento precedentemente selezionato in alcuni oggetti business.
RichardOD,

49

Dovresti anche usarlo <label for="checkbox1">Checkbox 1</label>perché le persone possono fare clic sul testo dell'etichetta e sulla casella stessa. È anche più facile da modellare e almeno in IE verrà evidenziato quando si sfogliano i controlli della pagina.

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

Questa non è solo una cosa "oh, potrei farlo". È un significativo miglioramento dell'esperienza utente. Anche se non tutti gli utenti sanno di poter fare clic sull'etichetta, molti lo faranno.


27

Sono sorpreso che nessuna di queste risposte abbia utilizzato le funzionalità MVC integrate per questo.

Ho scritto un post sul blog qui , che collega addirittura le etichette alla casella di controllo. Ho usato la cartella EditorTemplate per farlo in modo pulito e modulare.

Si finirà semplicemente con un nuovo file nella cartella EditorTemplate che assomiglia a questo:

@model SampleObject

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

nella tua visione reale, non sarà necessario eseguire questo ciclo, semplicemente 1 riga di codice:

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

Visita il mio post sul blog per maggiori dettagli.


1
Grazie! Everboyd utilizza ancora le vecchie forme. E quando invii puoi accedere al Modello senza tutta questa raccolta [] e request.form spazzatura - Questo è quello che stavo cercando. +1e + birra
Piotr Kula

2
Questa dovrebbe essere la risposta migliore alla domanda. Molto semplice e facile da implementare.
Johan Gunawan,

Ho lottato per qualche tempo per trovare una soluzione elegante a questo prima di trovare questa risposta. Ha qualche anno ma è ancora perfettamente valido nel 2016! Se si utilizza in diversi casi, utilizzare SelectListItem integrato come tipo generico che si adatta perfettamente a questo
Phil

SampleObject è un elenco? Come ad esempio: @ModelType List (Of Type)
JoshYates1980,

25

Ecco cosa ho fatto.

Visualizza:


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

controller:


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

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

Ho trovato i metodi helper Html. * Non così utili in alcuni casi e che è stato meglio farlo nel semplice vecchio HTML. Essendo uno di questi, l'altro che viene in mente sono i pulsanti di opzione.

Modifica: questo è in anteprima 5, ovviamente YMMV tra le versioni.


1
Voglio solo aggiungere un esempio alternativo: Object.Property =! String.IsNullOrEmpty (Request.Form ["NAME"]);
Gup3rSuR4c,

se deselezionato, va in eccezione
Xulfee,

10

Sembrano scegliere di leggere solo il primo valore, quindi questo è "vero" quando la casella di controllo è selezionata e "falso" quando è incluso solo il valore nascosto. Questo è facilmente recuperabile con codice come questo:

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

8

@Dylan Beattie Great Find !!! Ti ringrazio molto Per espandere ulteriormente, questa tecnica funziona perfettamente anche con l'approccio Visualizza modello. MVC è così bello, è abbastanza intelligente da associare una matrice di Guide a una proprietà con lo stesso nome dell'oggetto Modello associato alla Vista. Esempio:

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

Visualizza:

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

controller:

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

Chiunque abbia familiarità con la filosofia di View Model gioirà, questo funziona come un Champ!


Grazie, SampleViewModel ha chiarito un po 'di confusione nella risposta originale.
parlamento

6

Vorrei anche sottolineare che è possibile assegnare un nome diverso a ciascuna casella di controllo e che tale nome faccia parte dei parametri di actionresults.

Esempio,

Visualizza:

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

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

controller:

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

I valori dalla vista vengono passati all'azione poiché i nomi sono gli stessi.

So che questa soluzione non è l'ideale per il tuo progetto, ma ho pensato di lanciare l'idea là fuori.


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

Dal controller:

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

         var s = fc["checkbox1"];

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

4

Questo problema si sta verificando anche nella versione 1.0. Html.Checkbox () fa sì che un altro campo nascosto venga aggiunto con lo stesso nome / id della tua casella di controllo originale. E mentre stavo provando a caricare un array di checkbox usando document.GetElemtentsByName (), puoi indovinare come le cose venivano incasinate. È un bizzarro.


4

Da quello che posso raccogliere, il modello non vuole indovinare se controllato = vero o falso, ho aggirato questo impostando un attributo valore sull'elemento checkbox con jQuery prima di inviare il modulo in questo modo:

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

In questo modo, non è necessario un elemento nascosto solo per memorizzare il valore della casella di controllo.


4

So che questa domanda è stata scritta quando MVC3 non è stato pubblicato, ma per chiunque arrivi a questa domanda e utilizzi MVC3, potresti voler il modo "corretto" per farlo.

Mentre penso che faccia il tutto

Contains("true");

la cosa è ottima e pulita, e funziona su tutte le versioni di MVC, il problema è che non tiene conto della cultura (come se davvero importasse nel caso di un bool).

Il modo "corretto" per capire il valore di un bool, almeno in MVC3, è usare ValueProvider.

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

Lo faccio in uno dei siti dei miei clienti quando modifico i permessi:

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

Ora, la bellezza di questo è che puoi usarlo con qualsiasi tipo semplice, e sarà anche corretto in base alla Cultura (pensa a soldi, decimali, ecc.).

ValueProvider è ciò che viene utilizzato quando si formano le tue azioni in questo modo:

public ActionResult UpdatePermissions(bool permission_1, bool permission_2)

ma quando stai cercando di creare dinamicamente questi elenchi e controllare i valori, non conoscerai mai l'ID al momento della compilazione, quindi devi elaborarli al volo.


c'è un motivo per cui si utilizza Request.Params invece di aggiungere un parametro FormCollection al metodo?
SwissCoder il

Nessun motivo, non vedo davvero alcun vantaggio in un modo rispetto all'altro.
Dan VanWinkle,

1
Siamo spiacenti, in realtà c'è un motivo. Ho appena guardato il mio codice e sto usando questo metodo per 5 chiamate diverse e non mi andava di passare FormCollection da ciascun metodo. Questo è stato il modo più semplice per ottenere le chiavi senza dover passare nulla.
Dan VanWinkle,

Sì, sono nuovo di MVC. E ho letto da qualche parte che in generale è meglio usare FormCollection invece di Request.Params, come usare un modello tipizzato anziché FormCollection (quindi in realtà sarebbe meglio usare un modello ed evitare di usare Request.Params in generale). Ho notato che altre cose del codice non vengono utilizzate, come la variabile allPermissions e il keyAsInt. Grazie per aver risposto.
SwissCoder,

3

Il modo più semplice per farlo è quindi ...

Hai impostato il nome e il valore.

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

Quindi, inviando, prendi i valori delle caselle di controllo e salva in un array int. quindi la funzione LinQ appropriata. Questo è tutto..

[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

Come per la risposta di nautic20, basta semplicemente usare l'elenco di caselle di controllo di associazione del modello predefinito MVC con lo stesso nome di una proprietà collection di string / int / enum in ViewModel. Questo è tutto.

Ma è necessario sottolineare un problema. In ogni componente della casella di controllo, non inserire "Id" al suo interno, il che influirà sul binding del modello MVC.

Il codice seguente funzionerà per l'associazione del modello:

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

I seguenti codici non sono vincolanti per il modello (la differenza qui è assegnata all'ID per ogni casella di controllo)

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

Grazie per la risposta, ma la questione è juuuust un po 'vecchio. Sei anni, circa. Potresti voler modificare e specificare quale versione di MVC stai utilizzando. Ciò aiuterà chiunque utilizzi una versione più recente a trovare questa risposta.

Sono d'accordo con questo. La rimozione dell'ID univoco ha risolto questo problema dopo molti mal di testa e digrignamento dei denti
Ody

La versione di MVC che sto usando è .net MVC 4.0 per questo problema.
ChinaHelloWorld,

2

questo è quello che ho fatto per perdere i doppi valori quando ho usato Html.CheckBox (...

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

con 4 caselle selezionate, deselezionate, deselezionate, selezionate diventa true, false, false, false, true, false in true, false, false, true. proprio quello di cui avevo bisogno


0

Che ne dici di questo?

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

0

Quando utilizzo la casella di controllo HtmlHelper, preferisco di gran lunga lavorare con i dati del modulo della casella di controllo postati come array. Non so davvero perché, so che gli altri metodi funzionano, ma penso che preferisco semplicemente trattare le stringhe separate da virgola come un array il più possibile.

Quindi fare un test 'controllato' o vero sarebbe:

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

Fare un controllo falso sarebbe:

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

La differenza principale è usare GetValuespoiché ritorna come un array.


0

Fallo solo su $(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
Sfortunatamente, potresti ottenere strani risultati sul lato server per le persone con JavaScript disattivato (plug-in NoScript, vecchi telefoni cellulari, Lynx ...)
ArIck,

0

La mia soluzione è:

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

Ulteriori informazioni sono disponibili qui: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was-not-recognized-as-a- valid-booleano /


Non è necessario utilizzare alcun Javascript per questo ... var isChecked = formData [key] .Contains ("true");
Jammer,

0

Ho avuto quasi lo stesso problema ma il valore di ritorno del mio controller è stato bloccato con altri valori.

Ho trovato una soluzione semplice ma sembra un po 'approssimativa.

Prova a digitare il Viewbag.tuo controller e ora gli dai un nome comeViewbag.Checkbool

Ora passa alla vista e prova questo @Viewbag.Checkboolcon questo otterrai il valore dal controller.

I miei parametri del controller si presentano così:

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

e la mia casella di controllo si aggiornerà in questo modo:

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

0

Usando @mmacaulay, ho pensato a questo per bool:

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

Se selezionato attivo = vero

Se deselezionato attivo = falso

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.