Kotak centang untuk boolean nullable

97

Model saya memiliki boolean yang harus nullable

public bool? Foo
{
   get;
   set;
}

jadi di Razor cshtml saya, saya punya

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

kecuali itu tidak berhasil. Juga tidak melakukan casting dengan (bool). Jika aku melakukan

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

itu tidak membuat kesalahan, tetapi tidak mengikat ke model saya saat diposting dan foo disetel ke null. Apa cara terbaik untuk menampilkan Foo di halaman dan membuatnya terikat ke model saya di postingan?

DMulligan
sumber
2
Utas itu mengabaikan masalah, bahwa saya harus dapat mendeteksi null sebagai nilai ke-3. Pengiriman formulir dengan kotak centang tidak dicentang dan pengiriman formulir di mana kotak centang tidak ditampilkan adalah dua skenario berbeda yang harus diperhitungkan.
DMulligan

Jawaban:

107

Saya mendapatkannya untuk dikerjakan

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

dan kemudian membuat Boolean.cshtml di folder EditorTemplates saya dan menempel

@model bool?

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

dalam.

DMulligan
sumber
2
Bekerja untuk saya. Terima kasih!
Samuel
1
Saat bekerja dengan tipe boolean nullable, saya menggunakan ini '' '@ Html.CheckBoxFor (m => m.Foo.HasValue)' ''
Gaurav Aroraa
9
@GauravKumarArora Anda membuat kotak centang untuk HasValueproperti nullable , tetapi bukan nilai boolean yang sebenarnya. Ini akan membuat kotak centang jika terlepas dari apa nilai dasarnya. Jika nullable memiliki nilai, nilainya akan selalu benar.
Siewers
1
Ini jelas merupakan solusi yang lebih bersih dan fleksibel dari halaman ini. +1
T-moty
2
hanya catatan: 1. Anda harus menyimpan file ini di "Views / Shared / EditorTemplates" 2. Anda harus menamai file ini "Boolean.cshtml"
Jarrette
46

Menemukan jawaban dalam pertanyaan serupa - Rendering Nullable Bool as CheckBox . Ini sangat mudah dan hanya berfungsi:

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

Ini seperti jawaban yang diterima dari @afinkelstein kecuali kita tidak memerlukan 'template editor' khusus

resnyanskiy
sumber
1
Keuntungan lain dari ini atas jawaban yang diterima adalah Anda dapat memutuskan apakah nol harus sama dengan benar atau salah berdasarkan kasus per kasus seperti yang ditentukan logika aplikasi Anda, alih-alih memiliki satu default di seluruh aplikasi (atau membuat dua editor template!).
ChrisFox
25

Saya ada bool? IsDisabled { get; set; }di Model. Disisipkan jika di View.

<div class="inputClass" id="disabled">
    <div>
    @if(Model.IsDisabled==null)
    {
        Model.IsDisabled = false;
    }           
    @Html.CheckBoxFor(model => model.IsDisabled.Value)         
    </div>
</div> 
VitalyB
sumber
1
Tidak terlalu bagus karena Anda mengubah nilai dalam model dan dalam banyak kasus, false tidak sama dengan null. Saya memberi Anda poin karena ini percobaan yang bagus. Perhatikan bahwa saya juga setuju dengan Darin :)
Samuel
7
Sarankan untuk tidak memberikan poin karena ini adalah percobaan yang bagus karena mungkin salah menunjukkan kepada pembaca lain bahwa jawabannya berguna ketika ada masalah.
Chris
Alternatif yang terlihat lebih baik adalah menyetel Boolean ke false di pengontrol jika null sebelum merender halaman, tetapi tetap menggunakan ide .Value.
Graham Laight
15

Model saya memiliki boolean yang harus nullable

Mengapa? Ini tidak masuk akal. Kotak centang memiliki dua status: dicentang / tidak dicentang, atau Benar / Salah jika Anda mau. Tidak ada negara ketiga.

Atau tunggu Anda menggunakan model domain dalam tampilan Anda, bukan model tampilan? Itu masalahmu. Jadi, solusi bagi saya adalah menggunakan model tampilan di mana Anda akan mendefinisikan properti boolean sederhana:

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

dan sekarang Anda akan meminta tindakan pengontrol Anda meneruskan model tampilan ini ke tampilan dan membuat kotak centang yang sesuai.

Darin Dimitrov
sumber
26
Ada banyak sekali alasan berbeda mengapa masukan foo mungkin tidak muncul di halaman sama sekali. Jika memang muncul, saya dapat mengasumsikan ada nilai, tetapi saya harus dapat membedakan antara saat model diposting dengan foo sebagai tidak dicentang dan saat foo tidak ditampilkan.
DMulligan
@KMull, bagaimana dengan menyertakan properti boolean lain pada model tampilan Anda yang menunjukkan apakah Anda harus merender kotak centang untuk properti Foo dan yang dapat disimpan di bidang tersembunyi sehingga saat Anda mengirim kembali, Anda akan tahu apakah Anda harus mempertimbangkan nilai Foo atau tidak.
Darin Dimitrov
Saya melakukan itu pada awalnya, tetapi saya mendapatkan banyak sekali tipe nilai primitif ini yang mungkin atau mungkin tidak disertakan dalam formulir saya karena berbagai alasan. Menambahkan boolean untuk semuanya mulai terasa konyol. Ketika saya mencari nullables di Google, saya pikir itulah cara yang harus dilakukan.
DMulligan
4
@KMull, Anda dapat menulis pembantu, template editor, ... sehingga Anda tidak perlu mengulanginya untuk setiap properti yang memiliki properti seperti itu.
Darin Dimitrov
25
@DarinDimitrov, Anda jelas melewatkan atau mengabaikan fakta bahwa OP mengatakan dia harus dapat mewakili aspek "nol" dari bool. Anda terpaku pada fakta bahwa dia ingin menggunakan Kotak Centang. Alih-alih memberi tahu dia bahwa dia menulis "kode buruk" karena dia menggunakan model domainnya (yang mungkin atau mungkin TIDAK dia lakukan) dan mengatakan kepadanya bahwa kotak centang dicentang atau tidak dicentang, Anda seharusnya mencoba menjawab pertanyaannya atau tidak menjawab.
Andrew Steitz
7

Memperumit primitif dengan bidang tersembunyi untuk memperjelas apakah False atau Null tidak disarankan.

Kotak centang bukanlah yang seharusnya Anda gunakan - kotak centang hanya memiliki satu status: Dicentang . Jika tidak, bisa jadi apa saja.

Ketika bidang database Anda adalah boolean nullable ( bool?), UX harus menggunakan 3-Radio Buttons, di mana tombol pertama mewakili "Checked", tombol kedua mewakili "Not Checked" dan tombol ketiga mewakili null Anda, apa pun semantiknya null artinya. Anda dapat menggunakan daftar <select><option>tarik-turun untuk menyelamatkan real estat, tetapi pengguna harus mengeklik dua kali dan pilihannya tidak langsung jelas.

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

RadioButtonList, didefinisikan sebagai ekstensi bernama RadioButtonForSelectList, membangun tombol radio untuk Anda, termasuk nilai yang dipilih / dicentang, dan menyetel <div class="RBxxxx">sehingga Anda dapat menggunakan css untuk membuat tombol radio Anda menjadi horizontal (display: inline-block), vertikal, atau dalam gaya tabel (display: inline-block; width: 100px;)

Dalam model (saya menggunakan string, string untuk definisi kamus sebagai contoh pedagogis. Anda dapat menggunakan 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());
}
}
Jules Bartow
sumber
4

Bagi saya, solusinya adalah mengubah model tampilan. Pertimbangkan Anda sedang mencari faktur. Faktur ini bisa dibayar atau tidak. Jadi, penelusuran Anda memiliki tiga opsi: Berbayar, Belum Dibayar, atau "Saya tidak peduli".

Saya awalnya menetapkan ini sebagai bool? bidang:

public bool? PaidInvoices { get; set; }

Ini membuat saya tersandung pada pertanyaan ini. Saya akhirnya membuat tipe Enum dan saya menangani ini sebagai berikut:

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

Tentu saja saya membungkusnya dengan label dan memiliki teks yang ditentukan, maksud saya inilah opsi lain untuk dipertimbangkan.

Paul
sumber
Ini adalah representasi yang berguna. Jika Anda memiliki ruang terbatas, Anda juga dapat melakukannya dengan drop-down / pilih, kecuali dibutuhkan dua klik untuk memilih opsi. Seperti halnya pemilihan, tombol radio dapat terlihat sangat berbeda di antara browser, yang bisa menjadi desainer hebat.
Savage
4

Kotak centang hanya menawarkan 2 nilai (benar, salah). Boolean nullable memiliki 3 nilai (true, false, null) sehingga tidak mungkin melakukannya dengan kotak centang.

Pilihan yang baik adalah menggunakan drop-down.

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

Melihat

@Html.DropDownListFor(m => m.myValue, valueList)
Gudradain
sumber
1
Jika Anda tidak peduli apakah pengguna dapat menyetel semua 3 nilai yang mungkin, tidak masalah bahwa Anda tidak dapat merepresentasikan ketiganya hanya dengan menggunakan kotak centang.
Dave
@ David Saya pikir Anda benar-benar melewatkan intinya. OP memiliki bool? artinya dia harus memilih salah satu dari 3 kemungkinan nilai. Jika dia ingin hanya benar atau salah, dia harus menggunakan bool dan bukan bool?
Gudradain
1
Sebenarnya, komentar OP menunjukkan sebaliknya - bahwa jika ada kotak centang di formulir, dia tidak ingin null menjadi opsi sama sekali, karena opsi itu tertutup ketika ada formulir berbeda tanpa kotak centang.
Dave
Saya menggunakan strategi yang sama di halaman pencarian. Anda dapat mencari Ya, Tidak, atau mengabaikan boolean dalam pencarian Anda. Ada adalah kasus penggunaan! : D
Jess
4

Saya benar-benar akan membuat template untuk itu dan menggunakan template itu dengan EditorFor ().

Inilah cara saya melakukannya:

  1. Buat template Saya, yang pada dasarnya adalah tampilan parsial yang saya buat di direktori EditorTemplates, di bawah Bersama, di bawah Nama Tampilan sebagai (misalnya) 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. Gunakan seperti ini (dalam tampilan apa pun):

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

Itu saja.

Template sangat kuat dalam MVC, gunakanlah .. Anda dapat membuat seluruh halaman sebagai template, yang akan Anda gunakan dengan @Html.EditorFor(); asalkan Anda meneruskan model tampilan dalam ekspresi lambda ..

t_plusplus
sumber
3

Pendekatan terbersih yang dapat saya hasilkan adalah memperluas ekstensi yang tersedia HtmlHelpersementara masih menggunakan kembali fungsionalitas yang disediakan oleh kerangka kerja.

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

Saya bereksperimen dengan 'membentuk' ekspresi untuk memungkinkan langsung lolos ke native CheckBoxFor<Expression<Func<T, bool>>>tetapi saya rasa itu tidak mungkin.

Red Taz
sumber
Saya harus mengubah tanda tangan metode agar ini berfungsi untuk saya: Saya menggunakan public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)dan berhasil dengan luar biasa.
Pierre-Loup Pagniez
1

Saya memiliki masalah serupa di masa lalu.

Buat input Kotak Centang dalam HTML, dan setel atribut name = "Foo" Ini masih harus dikirim dengan benar.

<input type="checkbox" name="Foo" checked="@model.Foo.Value" /> Foo Checkbox<br />
Chris Lucian
sumber
Ini sepertinya bukan posting, saya masih mendapatkan null.
DMulligan
2
Ini mungkin memiliki masalah dengan pengikatan model
yoel halb
0

Cukup periksa nilai null dan kembalikan false padanya:

@{ bool nullableValue = ((Model.nullableValue == null) || (Model.nullableValue == false)) ? false : true; }
@Html.CheckBoxFor(model => nullableValue)
Rodrigo Boratto
sumber
Anda tahu bahwa metadata properti Anda akan hilang, bukan? Validasi sisi klien, nama bidang dan id, dll.
T-moty
0

Saya juga menghadapi masalah yang sama. Saya mencoba pendekatan berikut untuk menyelesaikan masalah karena saya tidak ingin mengubah DB dan menghasilkan EDMX lagi.

@{

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

 }

<label>@Html.CheckBoxFor(m => testVar)testVar</label><br />
Amit Kumar
sumber
0

Saat membuat EditorTemplate untuk model yang berisi bool nullable ...

  • Bagi bool nullable menjadi 2 boolean:

    // 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; }
  • Di dalam template editor:

    @Html.HiddenFor(m => m.FooIsNull)
    
    @if (Model.FooIsNull)
    {
        // Null means "checkbox is hidden"
        @Html.HiddenFor(m => m.FooCheckbox)
    }
    else
    {
        @Html.CheckBoxFor(m => m.FooCheckbox)
    }
  • Jangan memposting balik properti asli Foo, karena itu sekarang dihitung dari FooIsNull dan FooCheckbox.


sumber
0

Semua jawaban di atas datang dengan masalahnya sendiri. Cara termudah / terbersih IMO adalah membuat pembantu

MVC5 Razor

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

Pemakaian

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

Dalam skenario saya, kotak centang nullable berarti bahwa anggota staf belum mengajukan pertanyaan kepada klien, jadi pertanyaan itu dibungkus .checkbox-nullablesedemikian rupa sehingga Anda dapat menyesuaikan gaya dengan tepat dan membantu pengguna akhir mengidentifikasi bahwa itu bukan trueataufalse

CSS

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

Metode ekstensi:

        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);
        }
Jim
sumber
0

Tombol radio berguna, tetapi tidak memungkinkan Anda untuk membatalkan pilihan . Jika Anda menginginkan perilaku ini, pertimbangkan untuk menggunakan drop-down / pilih. Kode berikut akan menghasilkan SelectListdan ini mengikat berhasil ke boolean nullable:

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");
}
Kejam
sumber
-1
@{  bool testVar = ((bool)item.testVar ? true : false); }
  @Html.DisplayFor(modelItem => testVar)
edrdesigner
sumber
1
ini akan memunculkan pengecualian jika testVar adalah null.
danwyand
-1

Anda bisa mencoba seperti ini

@Html.TextBoxFor(m => m.foo, new {   type = "checkbox" })
Yağmur Bilgin
sumber
-4

Ini adalah pertanyaan lama, dan jawaban yang ada menjelaskan sebagian besar alternatif. Tapi ada satu pilihan sederhana, apakah Anda punya bool? di viewmodel Anda, dan Anda tidak peduli dengan null di UI Anda:

@Html.CheckBoxFor(m => m.boolValue ?? false);
Jeff Dege
sumber
2
Apakah kamu sudah mencobanya? Karena metode xxxFor mengharapkan ekspresi anggota, saya berani bertaruh bahwa ini akan menghasilkan kegagalan runtime. Ini mencoba untuk menentukan properti atau bidang target, bukan mengevaluasi apa pun pada saat ini.
JRoughan
2
run time error "Template hanya dapat digunakan dengan akses kolom, akses properti, indeks array dimensi tunggal, atau ekspresi pengindeks kustom parameter tunggal".
ePezhman