Pole wyboru dla wartości logicznej dopuszczającej wartość null


97

Mój model ma wartość logiczną, która musi mieć wartość zerową

public bool? Foo
{
   get;
   set;
}

więc w moim cshtml Razor mam

@Html.CheckBoxFor(m => m.Foo)

ale to nie działa. Ani też rzucanie za pomocą (bool). Jeśli zrobię

@Html.CheckBoxFor(m => m.Foo.Value)

to nie tworzy błędu, ale nie wiąże się z moim modelem po wysłaniu, a foo ma wartość null. Jaki jest najlepszy sposób na wyświetlenie Foo na stronie i powiązanie go z moim modelem w poście?



2
Ten wątek ignoruje problem, że muszę być w stanie wykryć wartość null jako trzecią wartość. Przesłanie formularza z odznaczonym polem wyboru i przesłanie formularza, w którym pole wyboru nie zostało wyświetlone, to dwa różne scenariusze, które należy wziąć pod uwagę.
— DMulligan

Odpowiedzi:


107

Mam to do pracy

@Html.EditorFor(model => model.Foo) 

a następnie tworzenie Boolean.cshtml w moim folderze EditorTemplates i przyklejanie

@model bool?

@Html.CheckBox("", Model.GetValueOrDefault())

wewnątrz.


2
Pracuje dla mnie. Dzięki!
— Samuel

1
Podczas pracy z typami logicznymi dopuszczającymi wartość null, użyłem tego '' '@ Html.CheckBoxFor (m => m.Foo.HasValue)' ''
— Gaurav Aroraa

9
@GauravKumarArora Tworzysz pole wyboru dla HasValuewłaściwości dopuszczającej wartość null , ale nie dla rzeczywistej wartości logicznej. Spowoduje to utworzenie zaznaczonego pola wyboru, jeśli niezależnie od wartości bazowej. Jeśli dopuszczalna wartość null ma wartość, zawsze będzie to prawda.
— Siewers

1
Jest to zdecydowanie bardziej przejrzyste i elastyczne rozwiązanie tej strony. +1
— T-moty

2
tylko uwaga: 1. musisz zapisać ten plik w "Views / Shared / EditorTemplates" 2. Musisz nazwać ten plik "Boolean.cshtml"
— Jarrette

46

Znalazłem odpowiedź na podobne pytanie - Rendering Nullable Bool as CheckBox . Jest to bardzo proste i po prostu działa:

@Html.CheckBox("RFP.DatesFlexible", Model.RFP.DatesFlexible ?? false)
@Html.Label("RFP.DatesFlexible", "My Dates are Flexible")

To jak zaakceptowana odpowiedź od @afinkelstein, z wyjątkiem tego, że nie potrzebujemy specjalnego „szablonu edytora”


1
Inną zaletą tego w porównaniu z akceptowaną odpowiedzią jest to, że możesz zdecydować, czy null powinno równać się prawdzie czy fałszowi w poszczególnych przypadkach, zgodnie z logiką twojej aplikacji, zamiast mieć jedną wartość domyślną w całej aplikacji (lub tworząc dwa edytory szablony!).
— ChrisFox

25

Mam bool? IsDisabled { get; set; }w Modelu. Wstawione, jeśli w widoku.

<div class="inputClass" id="disabled">
    <div>
    @if(Model.IsDisabled==null)
    {
        Model.IsDisabled = false;
    }           
    @Html.CheckBoxFor(model => model.IsDisabled.Value)         
    </div>
</div> 

1
Niezbyt dobre, ponieważ zmieniasz wartość w modelu iw wielu przypadkach fałsz nie jest równy null. Dałem ci punkt, ponieważ to niezła próba. Zauważ, że zgadzam się również z Darinem :)
— Samuel

7
Odradzam dawanie punktów, ponieważ jest to miła próba, ponieważ może niepoprawnie wskazać innym czytelnikom, że odpowiedź jest przydatna, gdy ma problemy.
— Chris

Lepszą alternatywą jest ustawienie wartości logicznej na false w kontrolerze, jeśli ma wartość null, przed renderowaniem strony, ale nadal należy używać idei .Value.
— Graham Laight

15

Mój model ma wartość logiczną, która musi mieć wartość zerową

Czemu? To nie ma sensu. Pole wyboru ma dwa stany: zaznaczone / niezaznaczone lub Prawda / Fałsz, jeśli chcesz. Nie ma trzeciego państwa.

A może poczekaj, gdy używasz modeli domeny w widokach zamiast modeli widoku? To Twój problem. Więc rozwiązaniem dla mnie jest użycie modelu widoku, w którym zdefiniujesz prostą właściwość boolowską:

public class MyViewModel
{
    public bool Foo { get; set; }
}

a teraz akcja kontrolera przekaże ten model widoku do widoku i wygeneruje odpowiednie pole wyboru.


26
Istnieje wiele różnych powodów, dla których dane wejściowe foo mogą w ogóle nie pojawiać się na stronie. Jeśli się pojawi, mogę założyć, że istnieje wartość, ale muszę być w stanie odróżnić, kiedy model jest wysyłany z foo jako niezaznaczony, a kiedy foo nie jest wyświetlany.
— DMulligan,

@KMulligan, co powiesz na włączenie innej właściwości boolowskiej do modelu widoku, wskazującej, czy powinieneś renderować pole wyboru dla właściwości Foo i które może być przechowywane w ukrytym polu, aby podczas ogłaszania zwrotnego wiedziałeś, czy powinieneś wziąć pod uwagę wartość Foo albo nie.
— Darin Dimitrov

Zrobiłem to pierwotnie, ale mam mnóstwo tych prymitywnych typów wartości, które mogą, ale nie muszą, być zawarte w moich formularzach z różnych powodów. Dodawanie do nich wszystkich wartości logicznych zaczęło wydawać się głupie. Kiedy przeszukałem nullable w Google, pomyślałem, że to jest właściwy sposób.
— DMulligan,

4
@KMulligan, możesz napisać pomoce, szablony edytorów, ... żebyś nie musiał tego powtarzać dla każdej właściwości, która ma takie właściwości.
— Darin Dimitrov

25
@DarinDimitrov, wyraźnie przeoczyłeś lub zignorowałeś fakt, że OP powiedział, że musi być w stanie przedstawić „zerowy” aspekt bool. Rozłączyłeś się z faktem, że chciał użyć CheckBoxFor. Zamiast powiedzieć mu, że napisał „zły kod”, ponieważ użył swojego modelu domeny (co mógł zrobić lub NIE) i powiedzieć mu, że pola wyboru są zaznaczone lub niezaznaczone, należy spróbować odpowiedzieć na jego pytanie lub nie odpowiedzieć.
— Andrew Steitz

7

Komplikowanie prymitywu ukrytymi polami w celu wyjaśnienia, czy Fałsz czy Null nie jest zalecane.

Pole wyboru nie jest tym, czego powinieneś używać - tak naprawdę ma tylko jeden stan: zaznaczone . W przeciwnym razie może to być wszystko.

Gdy twoje pole bazy danych ma wartość logiczną ( bool?) dopuszczającą wartość null , interfejs użytkownika powinien używać przycisków 3-radiowych, gdzie pierwszy przycisk reprezentuje „zaznaczone”, drugi przycisk reprezentuje „nie zaznaczono”, a trzeci przycisk reprezentuje wartość null, niezależnie od semantyki null oznacza. Możesz użyć <select><option>listy rozwijanej, aby zapisać nieruchomość, ale użytkownik musi kliknąć dwa razy, a wybory nie są tak natychmiastowo jasne.

  1     0      null 
True  False  Not Set
Yes   No     Undecided
Male  Female Unknown
On    Off    Not Detected

RadioButtonList, zdefiniowane jako rozszerzenie o nazwie RadioButtonForSelectList, buduje przyciski opcji, w tym wybraną / zaznaczoną wartość, i ustawia <div class="RBxxxx">tak, aby można było użyć css, aby ustawić przyciski radiowe w poziomie (display: inline-block), w pionie lub w formie tabeli (display: inline-block; width: 100px;)

W modelu (używam string, string dla definicji słownika jako przykładu pedagogicznego. Możesz użyć bool ?, string)

public IEnumerable<SelectListItem> Sexsli { get; set; }
       SexDict = new Dictionary<string, string>()
        {
                { "M", "Male"},
                { "F", "Female" },
                { "U", "Undecided" },

        };

        //Convert the Dictionary Type into a SelectListItem Type
        Sexsli = SexDict.Select(k =>
              new SelectListItem
              {
                  Selected = (k.Key == "U"),
                  Text = k.Value,
                  Value = k.Key.ToString()
              });

<fieldset id="Gender">
<legend id="GenderLegend" title="Gender - Sex">I am a</legend>
    @Html.RadioButtonForSelectList(m => m.Sexsli, Model.Sexsli, "Sex") 
        @Html.ValidationMessageFor(m => m.Sexsli)
</fieldset>

public static class HtmlExtensions
{
public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression,
    IEnumerable<SelectListItem> listOfValues,
    String rbClassName = "Horizontal")
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var sb = new StringBuilder();

if (listOfValues != null)
{
    // Create a radio button for each item in the list 
    foreach (SelectListItem item in listOfValues)
    {
        // Generate an id to be given to the radio button field 
        var id = string.Format("{0}_{1}", metaData.PropertyName, item.Value);

        // Create and populate a radio button using the existing html helpers 
        var label = htmlHelper.Label(id, HttpUtility.HtmlEncode(item.Text));

        var radio = String.Empty;

        if (item.Selected == true)
        {
            radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id, @checked = "checked" }).ToHtmlString();
        }
        else
        {
            radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id }).ToHtmlString();

        }// Create the html string to return to client browser
        // e.g. <input data-val="true" data-val-required="You must select an option" id="RB_1" name="RB" type="radio" value="1" /><label for="RB_1">Choice 1</label> 

        sb.AppendFormat("<div class=\"RB{2}\">{0}{1}</div>", radio, label, rbClassName);
    }
}

return MvcHtmlString.Create(sb.ToString());
}
}

4

Dla mnie rozwiązaniem była zmiana modelu widoku. Zastanów się, czy szukasz faktur. Te faktury mogą być opłacone lub nie. Twoje wyszukiwanie ma więc trzy opcje: płatne, bezpłatne lub „nie obchodzi mnie to”.

Miałem to pierwotnie ustawione jako bool? pole:

public bool? PaidInvoices { get; set; }

To sprawiło, że natknąłem się na to pytanie. Skończyło się na utworzeniu typu Enum i załatwiłem to w następujący sposób:

@Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.NotSpecified, new { @checked = true })
@Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.Yes) 
@Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.No)

Oczywiście miałem je zawinięte w etykiety i określony tekst, mam na myśli tylko inną opcję do rozważenia.


To jest przydatna reprezentacja. Jeśli masz ograniczoną przestrzeń, możesz to również osiągnąć za pomocą menu rozwijanego / wybierz, z tym wyjątkiem, że wybranie opcji wymaga dwóch kliknięć. Podobnie jak w przypadku selekcji, przyciski opcji mogą wyglądać bardzo różnie w różnych przeglądarkach, co może być piekłem projektanta.
— Savage

4

Pole wyboru oferuje tylko 2 wartości (prawda, fałsz). Wartość logiczna dopuszczająca wartość null ma 3 wartości (prawda, fałsz, null), więc nie można tego zrobić za pomocą pola wyboru.

Dobrym rozwiązaniem jest skorzystanie z listy rozwijanej.

Model

public bool? myValue;
public List<SelectListItem> valueList;

Kontroler

model.valueList = new List<SelectListItem>();
model.valueList.Add(new SelectListItem() { Text = "", Value = "" });
model.valueList.Add(new SelectListItem() { Text = "Yes", Value = "true" });
model.valueList.Add(new SelectListItem() { Text = "No", Value = "false" });

Widok

@Html.DropDownListFor(m => m.myValue, valueList)

1
Jeśli nie zależy Ci na tym, aby użytkownik mógł ustawić wszystkie 3 możliwe wartości, nie ma znaczenia, że ​​nie możesz przedstawić wszystkich 3 za pomocą tylko pola wyboru.
— Dave,

@Dave Myślę, że całkowicie przegapiłeś punkt. OP ma bool? co oznacza, że ​​musi wybrać jedną z 3 możliwych wartości. Jeśli chciał mieć tylko prawdę lub fałsz, powinien użyć bool, a nie bool?
— Gudradain

1
Właściwie komentarz OP wskazuje na coś przeciwnego - że jeśli w formularzu znajduje się pole wyboru, nie chce, aby wartość null była w ogóle opcją, ponieważ ta opcja jest zakryta, gdy istnieje inny formularz bez pola wyboru.
— Dave,

Użyłem tej samej strategii na stronie wyszukiwania. Możesz wyszukać tak, nie lub zignorować wartość logiczną w wyszukiwaniu. Nie jest to przypadek użycia! : D
— Jess

4

Właściwie stworzyłbym dla niego szablon i użyłbym tego szablonu z EditorFor ().

Oto jak to zrobiłem:

  1. Utwórz mój szablon, który jest w zasadzie częściowym widokiem, który utworzyłem w katalogu EditorTemplates, w obszarze Udostępnione, w obszarze Widoki nazwij go jako (na przykład) CheckboxTemplate:

    @using System.Globalization    
    @using System.Web.Mvc.Html    
    @model bool?
    
    @{
        bool? myModel = false;
        if (Model.HasValue)
        {
            myModel = Model.Value;
        }
    }
    
    <input type="checkbox" checked="@(myModel)" name="@ViewData.TemplateInfo.HtmlFieldPrefix" value="True" style="width:20px;" />
  2. Użyj tego w ten sposób (w dowolnym widoku):

    @ Html.EditorFor (x => x.MyNullableBooleanCheckboxToBe, "CheckboxTemplate")

To wszystko.

Szablony są tak potężne w MVC, używaj ich .. Możesz stworzyć całą stronę jako szablon, którego użyjesz z @Html.EditorFor(); pod warunkiem, że przekażesz jego model widoku w wyrażeniu lambda.


3

Najczystszym podejściem, jakie mogłem wymyślić, jest rozszerzenie dostępnych rozszerzeń, HtmlHelperprzy jednoczesnym ponownym wykorzystaniu funkcji zapewnianych przez framework.

public static MvcHtmlString CheckBoxFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, bool?>> expression, IDictionary<string, object> htmlAttributes) {

    ModelMetadata modelMeta = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    bool? value = (modelMeta.Model as bool?);

    string name = ExpressionHelper.GetExpressionText(expression);

    return htmlHelper.CheckBox(name, value ?? false, htmlAttributes);
}

Eksperymentowałem z „kształtowaniem” wyrażenia, aby umożliwić proste przejście do tubylca, CheckBoxFor<Expression<Func<T, bool>>>ale nie sądzę, żeby było to możliwe.


Musiałem zmienić sygnaturę metody, żeby to zadziałało: użyłem public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)i zadziałało cudownie.
— Pierre-Loup Pagniez

1

W przeszłości miałem podobny problem.

Utwórz pole wyboru w HTML i ustaw atrybut name = "Foo". Powinno to nadal być wysyłane poprawnie.

<input type="checkbox" name="Foo" checked="@model.Foo.Value" /> Foo Checkbox<br />

To nie wydaje się być wysyłane, wciąż otrzymuję wartość null.
— DMulligan,

2
To może mieć problemy z wiązaniem modelu
— yoel halb

0

Po prostu sprawdź wartość null i zwróć do niej false:

@{ bool nullableValue = ((Model.nullableValue == null) || (Model.nullableValue == false)) ? false : true; }
@Html.CheckBoxFor(model => nullableValue)

Wiesz, że metadane Twojej nieruchomości zostaną utracone, prawda? Walidacja po stronie klienta, nazwa i identyfikator pola, itp.
— T-moty

0

Ja też stanąłem przed tym samym problemem. Wypróbowałem następujące podejście, aby rozwiązać problem, ponieważ nie chcę zmieniać bazy danych i ponownie generować EDMX.

@{

   bool testVar = (Model.MYVar ? true : false);

 }

<label>@Html.CheckBoxFor(m => testVar)testVar</label><br />

0

Podczas tworzenia EditorTemplate dla modelu, który zawiera wartość bool dopuszczającą wartość null ...

  • Podziel wartość zerową bool na 2 wartości logiczne:

    // Foo is still a nullable boolean.
    public bool? Foo 
    {
        get 
        { 
            if (FooIsNull)
                return null;
    
            return FooCheckbox;  
        }
        set
        {
            FooIsNull   = (value == null);
            FooCheckbox = (value ?? false);
        }
    }
    
    // These contain the value of Foo. Public only so they are visible in Razor views.
    public bool FooIsNull { get; set; }
    public bool FooCheckbox { get; set; }
  • W szablonie edytora:

    @Html.HiddenFor(m => m.FooIsNull)
    
    @if (Model.FooIsNull)
    {
        // Null means "checkbox is hidden"
        @Html.HiddenFor(m => m.FooCheckbox)
    }
    else
    {
        @Html.CheckBoxFor(m => m.FooCheckbox)
    }
  • Nie ogłaszaj zwrotnie oryginalnej właściwości Foo, ponieważ jest ona teraz obliczana na podstawie FooIsNull i FooCheckbox.


0

Wszystkie powyższe odpowiedzi miały swoje własne problemy. Najłatwiejszym / najczystszym sposobem IMO jest stworzenie pomocnika

Brzytwa MVC5

App_Code / Helpers.cshtml

@helper CheckBoxFor(WebViewPage page, string propertyName, bool? value, string htmlAttributes = null)
{
    if (value == null)
    {
        <div class="checkbox-nullable">
            <input type="checkbox" @page.Html.Raw(htmlAttributes)>
        </div>
    }
    else if (value == true)
    {
        <input type="checkbox" value="true" @page.Html.Raw(htmlAttributes) checked>
    }
    else
    {
        <input type="checkbox" value="false" @page.Html.Raw(htmlAttributes)>
    }
}

Stosowanie

@Helpers.CheckBoxFor(this, "IsPaymentRecordOk", Model.IsPaymentRecordOk)

W moim scenariuszu pole wyboru dopuszczające wartość zerową oznacza, że ​​członek personelu nie zadał jeszcze pytania klientowi, więc jest opakowany .checkbox-nullabletak, aby można było odpowiednio stylizować i pomóc użytkownikowi końcowemu zidentyfikować, że nie jest to trueanifalse

CSS

.checkbox-nullable {
    border: 1px solid red;
    padding: 3px;
    display: inline-block;
}

0

Metody rozszerzeń:

        public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression)
        {
            return htmlHelper.CheckBoxFor<TModel>(expression, null);
        }
        public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            bool? isChecked = null;
            if (metadata.Model != null)
            {
                bool modelChecked;
                if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
                {
                    isChecked = modelChecked;
                }
            }
            return htmlHelper.CheckBox(ExpressionHelper.GetExpressionText(expression), isChecked??false ,  htmlAttributes);
        }

0

Przyciski opcji są przydatne, ale nie pozwalają na odznaczenie . Jeśli chcesz tego zachowania, rozważ użycie listy rozwijanej / wybierz. Poniższy kod wygeneruje SelectListi powiąże się pomyślnie z wartością logiczną dopuszczającą wartość null:

public static SelectList GetBoolTriState()
{
    var items = new List<SelectListItem>
    {
        new SelectListItem {Value = "", Text = ""},
        new SelectListItem {Value = "True", Text = "Yes"},
        new SelectListItem {Value = "False", Text = "No"},
    };

    return new SelectList(items, "Value", "Text");
}

-1
@{  bool testVar = ((bool)item.testVar ? true : false); }
  @Html.DisplayFor(modelItem => testVar)

1
spowoduje to zgłoszenie wyjątku, jeśli testVar ma wartość null.
— danwyand


-4

To stare pytanie, a istniejące odpowiedzi opisują większość alternatyw. Ale jest jedna prosta opcja, jeśli masz bool? w Twoim modelu widoku i nie przejmujesz się null w swoim interfejsie użytkownika:

@Html.CheckBoxFor(m => m.boolValue ?? false);

2
Próbowałeś tego? Ponieważ metody xxxFor oczekują wyrażenia członkowskiego, założę się, że spowoduje to awarię środowiska wykonawczego. Próbuje określić docelową właściwość lub pole, nie oceniając niczego w tym momencie.
— JRoughan

2
błąd czasu wykonywania „Szablony mogą być używane tylko z dostępem do pól, dostępem do właściwości, indeksem tablicy jednowymiarowej lub niestandardowymi wyrażeniami indeksatora z jednym parametrem”.
— ePezhman
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.