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?

Uwe Keim
źródło
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.

Dylan Beattie
źródło
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");
Andrea Balducci
źródło
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.

Simon_Weaver
źródło
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.

Simon_Weaver
źródło
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.

Shawn Mclean
źródło
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.

mmacaulay
źródło
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");
Puszysty
źródło
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!

nautic20
źródło
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ł.

Darcy
źródło
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";
         }
    }
bluwater2001
źródło
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.

Farhan Zia
źródło
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.

BraveNewMath
źródło
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.

Dan VanWinkle
źródło
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);
        }
kk-dev11
źródło
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>
<% } %>
ChinaHelloWorld
źródło
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

Jeroen
źródło
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.”);
Skadoosh
źródło
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ę.

eyesnz
źródło
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);

    }
});
doronAv
źródło
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 /

pawlom84
źródło
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()" />
treborian
źródło
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

Ravi Ram
źródło