Jak obsługiwać pola wyboru w formularzach ASP.NET MVC?


246

Uwaga: to pytanie ma ponad dziewięć lat!

Najlepszą opcją jest wyszukiwanie nowszych pytań lub wyszukiwanie poniższych odpowiedzi w poszukiwaniu konkretnej wersji MVC, ponieważ wiele odpowiedzi tutaj jest już nieaktualnych.

Jeśli znajdziesz odpowiedź, która działa dla Twojej wersji, upewnij się, że zawiera ona wersję używanego MVC.
(Oryginalne pytanie zaczyna się poniżej)


Wydaje mi się to trochę dziwne, ale o ile wiem, tak to robisz.

Mam kolekcję obiektów i chcę, aby użytkownicy wybrali jeden lub więcej z nich. To mówi do mnie „formularz z polami wyboru”. Moje obiekty nie mają pojęcia „wybranego” (są to podstawowe POCO utworzone przez deserializację wywołania wcf). Więc wykonuję następujące czynności:

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

W widoku:

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

I w kontrolerze to jedyny sposób, w jaki mogę zobaczyć, jakie obiekty sprawdził użytkownik:

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

Jest dziwaczny, a po drugie, w przypadku elementów, które sprawdził użytkownik, FormCollection podaje jego wartość jako „prawda fałsz”, a nie tylko prawda.

Oczywiście czegoś mi brakuje. Myślę, że jest to zbudowane z myślą o tym, że obiekty w kolekcji, na które działa się w formularzu HTML, są aktualizowane przy użyciu UpdateModel()lub za pośrednictwem ModelBinder.

Ale moje obiekty nie są do tego skonfigurowane; Czy to oznacza, że ​​to jedyny sposób? Czy jest na to inny sposób?


2
Inni mogą uznać to rozwiązanie za przydatne: stackoverflow.com/questions/3291501/...
Aaron

Odpowiedzi:


262

Html.CheckBox robi coś dziwnego - jeśli wyświetlisz źródło na wynikowej stronie, zobaczysz, <input type="hidden" />że obok każdego pola wyboru generowane jest źródło , które wyjaśnia „prawdziwe fałszywe” wartości, które widzisz dla każdego elementu formularza.

Wypróbuj to, co zdecydowanie działa na ASP.NET MVC Beta, ponieważ właśnie go wypróbowałem.

Umieść to w widoku zamiast używać 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" />
<%}%>

Wszystkie pola wyboru są wywoływane selectedObjects, a valuekażde pole wyboru jest identyfikatorem GUID odpowiedniego obiektu.

Następnie opublikuj następującą akcję kontrolera (lub coś podobnego, co robi coś użytecznego zamiast Response.Write ())

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

Ten przykład po prostu zapisze identyfikatory GUID zaznaczonych pól; ASP.NET MVC mapuje wartości GUID wybranych pól wyboru do Guid[] selectedObjectsparametru dla Ciebie, a nawet analizuje ciągi z kolekcji Request.Form w utworzonych obiektach GUID, co moim zdaniem jest całkiem miłe.


Tak! To właśnie musiałem zrobić dla moich aplikacji!
Adhip Gupta

70
wtf. ukryte pola wejściowe z SAMĄ nazwą jako formantem. jego ViewState 2.0!
Simon_Weaver

3
Jest to również obecne w wersji 1.0. Spójrz na przesłaną odpowiedź @ andrea-balducci, która daje ci inteligentny sposób na poradzenie sobie z tym. Jeśli pole wyboru nie jest zaznaczone, wynikowy pobrany tekst powinien być „fałszywy fałsz” - jest to dobre obejście dla martwych mózgów przeglądarek ...
Bryan Rehbein

77
Niespodzianka? Wtf? Ukryte wejście ma taką samą nazwę jak pole wyboru - jeśli pole wyboru o tej samej nazwie nie jest zaznaczone, wówczas jego wartość nie jest księgowana, podczas gdy wartość ukrytego jest wysyłana. Gdy przeglądarka po raz pierwszy napotka nazwany element, użyje tej wartości i zignoruje wszystkie inne elementy o tej samej nazwie. Gwarantuje to przesłanie wartości: prawda, jeśli pole wyboru jest zaznaczone (zakładając, że znajduje się nad ukrytym elementem) i fałsz, jeśli pole wyboru nie jest zaznaczone (puste pole wyboru jest ignorowane, a ukryte staje się cofnięciem). Prawdziwy wtf jest powodem, dla którego nikt inny tego nie zauważył.
nerraga,

19
@nerraga: Mylisz się: poprawny HTML zawiera wiele elementów formularza o tej samej nazwie; a jeśli tak, wszystkie elementy są publikowane i nie jestem pewien, czy kolejność jest faktycznie zdefiniowana (choć w praktyce prawdopodobnie będzie po prostu w kolejności stron). Używając słowa „fałsz”, ponieważ wartość jest nieco myląca, więc tak, jest to WTF - lepszym, mniej mylącym wyborem byłoby coś takiego jak „istnieje” lub jakiś bezsensowny token jak myślnik.
Eamon Nerbonne,

95

HtmlHelper dodaje ukryte dane wejściowe, aby powiadomić kontroler o statusie niezaznaczonym. Aby mieć poprawny status sprawdzony:

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

1
To najłatwiejsze podejście do tego problemu i zawsze tego używam. Umożliwia korzystanie z metod pomocniczych i uwzględnianie zachowania MVC platformy ASP.NET.
Nick Daniels,

8
.. lub w przypadku niezaznaczonych: bool bChecked = form [key] .Equals („false”); Wykonanie operacji .Contains („false”) kończy się niepowodzeniem, ponieważ wartość true to „true, false”.
Mark Robinson

54

Jeśli zastanawiasz się, DLACZEGO wstawiają ukryte pole o tej samej nazwie co pole wyboru, powód jest następujący:

Komentarz z kodu źródłowego MVCBetaSource \ MVC \ src \ MvcFutures \ Mvc \ ButtonsAndLinkExtensions.cs

Renderuj dodatkowo <input type="hidden".../>pole wyboru. Dotyczy to scenariuszy, w których niezaznaczone pola wyboru nie są wysyłane w żądaniu. Wysłanie ukrytych danych wejściowych pozwala wiedzieć, że pole wyboru było obecne na stronie podczas przesyłania żądania.

Myślę, że za kulisami muszą to wiedzieć, aby powiązać parametry z metodami działania kontrolera. Mógłbym wtedy mieć trójstanowy stan logiczny, jak przypuszczam (związany z nollable parametrem bool). Nie próbowałem tego, ale mam nadzieję, że tak właśnie zrobili.


4
Tak, jest to przydatne w scenariuszach, w których siatki stronicowane itp. I chcesz odznaczyć element, który został wcześniej wybrany w jakimś obiekcie biznesowym.
RichardOD

49

Powinieneś także użyć, <label for="checkbox1">Checkbox 1</label>ponieważ wtedy ludzie mogą kliknąć tekst etykiety, a także samo pole wyboru. Łatwiej jest również stylizować i przynajmniej w IE zostanie podświetlony, gdy będziesz przechodzić między kartami kontrolnymi strony.

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

To nie jest tylko kwestia „och, mógłbym to zrobić”. Jest to znacząca poprawa komfortu użytkowania. Nawet jeśli nie wszyscy użytkownicy wiedzą, że mogą kliknąć etykietę, wielu z nich to zrobi.


27

Dziwię się, że żadna z tych odpowiedzi nie wykorzystała do tego wbudowanych funkcji MVC.

Napisałem blogu na ten temat tutaj , który nawet faktycznie łączy etykiet do wyboru. Użyłem EditorTemplate folder do osiągnięcia tego w czystym i sposób modułowy.

Po prostu skończy się nowy plik w folderze EditorTemplate, który wygląda następująco:

@model SampleObject

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

w twoim rzeczywistym widoku nie będzie potrzeby zapętlać tego, wystarczy 1 linia kodu:

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

Odwiedź mój wpis na blogu, aby uzyskać więcej informacji.


1
Dziękuję Ci! Everboyd wciąż używa starych form. Po przesłaniu możesz uzyskać dostęp do Modelu bez całej kolekcji [] i śmieci request.form - tego właśnie szukałem. +1i + piwo
Piotr Kula,

2
To powinna być najlepsza odpowiedź na pytanie. Bardzo prosty i łatwy do wdrożenia.
Johan Gunawan

Przez jakiś czas starałem się znaleźć eleganckie rozwiązanie tego problemu, zanim znalazłem tę odpowiedź. Ma kilka lat, ale nadal doskonale obowiązuje w 2016 roku! Jeśli używasz w kilku przypadkach, użyj wbudowanego SelectListItem jako typ ogólny, który jest idealnie do tego odpowiedni
Phil

Czy SampleObject jest listą? Takich jak: @ModelType List (Of Type)
JoshYates1980

25

Oto co robiłem.

Widok:


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

Kontroler:


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

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

Znalazłem, że metody pomocnicze HTML. * Nie są tak przydatne w niektórych przypadkach i że lepiej byłoby, gdybym robił to w zwykłym starym HTML. Jest to jeden z nich, drugi przychodzi na myśl przyciski radiowe.

Edycja: dotyczy wersji zapoznawczej 5, oczywiście między wersjami YMMV.


1
Chcę tylko dodać alternatywny przykład: Object.Property =! String.IsNullOrEmpty (Request.Form ["NAME"]);
Gup3rSuR4c

jeśli niezaznaczone, to przechodzi do wyjątku
Xulfee

10

Wygląda na to, że zdecydowali się czytać tylko pierwszą wartość, więc jest to „prawda”, gdy pole wyboru jest zaznaczone, i „fałsz”, gdy zawarta jest tylko wartość ukryta. Można to łatwo pobrać za pomocą takiego kodu:

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

8

@Dylan Beattie Great Find !!! Bardzo ci dziękuję Aby jeszcze bardziej rozwinąć tę technikę, działa ona również doskonale w podejściu Model widoku. MVC jest tak fajny, że jest wystarczająco inteligentny, aby powiązać tablicę prowadnic z właściwością o tej samej nazwie obiektu Model powiązanego z widokiem. Przykład:

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

Widok:

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

Kontroler:

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

Każdy, kto zna filozofię View Model, będzie się cieszył, działa to jak Bohater!


Dzięki, Twój SampleViewModel wyjaśnił pewne zamieszanie w oryginalnej odpowiedzi.
parlament

6

Chciałbym również zauważyć, że możesz nazwać każde pole wyboru inną nazwą i włączyć tę nazwę do parametrów akcji działania.

Przykład,

Widok:

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

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

Kontroler:

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

Wartości z widoku są przekazywane do akcji, ponieważ nazwy są takie same.

Wiem, że to rozwiązanie nie jest idealne dla twojego projektu, ale pomyślałem, że przedstawię ten pomysł.


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

Z kontrolera:

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

         var s = fc["checkbox1"];

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

4

Ten problem występuje również w wersji 1.0. Html.Checkbox () powoduje dodanie innego ukrytego pola o tej samej nazwie / identyfikatorze, co oryginalne pole wyboru. A gdy próbowałem załadować tablicę pól wyboru za pomocą document.GetElemtentsByName (), możesz zgadywać, jak się sprawy popsuły. To dziwne.


4

Z tego, co mogę zebrać, model nie chce zgadywać, czy zaznaczone = prawda czy fałsz, obejrzałem to, ustawiając atrybut wartości elementu checkbox za pomocą jQuery przed przesłaniem formularza w następujący sposób:

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

W ten sposób nie potrzebujesz ukrytego elementu tylko do przechowywania wartości pola wyboru.


4

Wiem, że to pytanie zostało napisane, gdy MVC3 nie było dostępne, ale dla każdego, kto podejdzie do tego pytania i używa MVC3, możesz chcieć „poprawnego” sposobu na zrobienie tego.

Chociaż myślę, że to robi całość

Contains("true");

rzecz jest świetna i czysta i działa na wszystkich wersjach MVC, problem polega na tym, że nie bierze pod uwagę kultury (tak jakby to naprawdę miało znaczenie w przypadku bool).

„Prawidłowym” sposobem obliczenia wartości bool, przynajmniej w MVC3, jest użycie ValueProvider.

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

Robię to w jednej z witryn mojego klienta, kiedy edytuję uprawnienia:

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

Teraz piękno tego polega na tym, że możesz używać tego z dowolnym prostym typem, a nawet będzie on poprawny w oparciu o kulturę (pomyśl o pieniądzach, liczbach dziesiętnych itp.).

ValueProvider jest używany podczas tworzenia akcji w następujący sposób:

public ActionResult UpdatePermissions(bool permission_1, bool permission_2)

ale gdy próbujesz dynamicznie budować te listy i sprawdzać wartości, nigdy nie poznasz identyfikatora w czasie kompilacji, więc musisz je przetwarzać w locie.


czy istnieje powód, dla którego używasz Request.Params zamiast dodawać parametr FormCollection do metody?
SwissCoder,

Bez powodu, tak naprawdę nie widzę żadnej korzyści z jednej strony nad drugą.
Dan VanWinkle

1
Przepraszam, jest właściwie powód. Właśnie spojrzałem na mój kod i używam tej jednej metody do 5 różnych wywołań i nie miałem ochoty przekazywać FormCollection z każdej metody. To był najłatwiejszy sposób na zdobycie kluczy bez konieczności przekazywania ich dookoła.
Dan VanWinkle

tak, jestem nowy w MVC. Przeczytałem gdzieś, że ogólnie lepiej jest używać FormCollection zamiast Request.Params, tak samo jak przy użyciu Modelu zamiast FormCollection (tak więc najlepiej byłoby użyć modelu i unikać generowania Request.Params w ogóle). Zauważyłem, że inne elementy kodu nie są używane, takie jak zmienna allPermissions i keyAsInt. Dzięki za odpowiedź.
SwissCoder

3

Najłatwiej to zrobić ...

Ustawiasz nazwę i wartość.

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

Następnie po przesłaniu chwyć wartości pól wyboru i zapisz w tablicy int. następnie odpowiednia funkcja LinQ. Otóż ​​to..

[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

Tak jak odpowiedź nautic20, po prostu użyj domyślnej listy pól wyboru wiązania modelu MVC o takiej samej nazwie jak właściwość kolekcji string / int / enum w ViewModel. To jest to.

Ale należy zwrócić uwagę na jedną kwestię. W każdym elemencie pola wyboru nie należy umieszczać w nim „Id”, co wpłynie na wiązanie modelu MVC.

Poniższy kod będzie działał dla wiązania modelu:

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

Poniższe kody nie będą wiążące dla modelu (tutaj różnica polega na przypisaniu identyfikatora do każdego pola wyboru)

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

Dzięki za odpowiedzi, ale to pytanie jest juuuust nieco stary. Około sześciu lat. Możesz edytować i określić, której wersji MVC używasz. Pomoże to każdemu, kto korzysta z nowszej wersji, zlokalizować tę odpowiedź.

Popieram to. Usunięcie unikalnego identyfikatora rozwiązało ten problem po wielu bólach głowy i zgrzytaniu zębami
Ody

Używana przeze mnie wersja MVC to .net MVC 4.0 dla tego problemu.
ChinaHelloWorld,

2

to właśnie zrobiłem, aby utracić podwójne wartości podczas korzystania z Html.CheckBox (...

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

z 4 zaznaczonymi polami, niezaznaczone, niezaznaczone, zaznaczone zmienia się w true, false, false, false, true, false w true, false, false, true. dokładnie to, czego potrzebowałem


0

Co powiesz na coś takiego?

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

0

Korzystając z pola wyboru HtmlHelper, zdecydowanie wolę pracować z opublikowanymi danymi formularza pola wyboru jako tablicą. Naprawdę nie wiem dlaczego, wiem, że inne metody działają, ale myślę, że po prostu wolę traktować ciągi rozdzielone przecinkami jak najwięcej tablic.

Zatem wykonanie „sprawdzonego” lub prawdziwego testu byłoby:

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

Wykonanie fałszywej kontroli byłoby:

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

Główną różnicą jest użycie, GetValuesponieważ zwraca jako tablicę.


0

Po prostu zrób to na $(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
Niestety, możesz uzyskać dziwne wyniki po stronie serwera dla osób z wyłączoną
obsługą

0

Moje rozwiązanie to:

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

Więcej można znaleźć tutaj: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was-not-recognized-as-a- valid-boolean /


Nie musisz używać JavaScript w tym celu ... var isChecked = formData [klucz] .Contains ("true");
Jammer

0

Miałem prawie ten sam problem, ale zwracana wartość mojego kontrolera została zablokowana innymi wartościami.

Znalazłem proste rozwiązanie, ale wydaje się nieco szorstkie.

Spróbuj wpisać Viewbag.kontroler, a teraz nadasz mu nazwę „jak”Viewbag.Checkbool

Teraz przejdź do widoku i spróbuj tego @Viewbag.Checkbool , uzyskując wartość z kontrolera.

Moje parametry kontrolera wyglądają następująco:

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

a moje pole wyboru zaktualizuje się w następujący sposób:

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

0

Korzystając z @mmacaulay, wymyśliłem to dla bool:

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

Jeśli zaznaczone aktywne = prawda

Jeśli niezaznaczone aktywne = false

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.