web-dev-qa-db-fra.com

Case à cocher pour un booléen nullable

Mon modèle a un booléen qui doit être nullable

public bool? Foo
{
   get;
   set;
}

donc dans mon rasoir cshtml j'ai

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

sauf que ça ne marche pas. Pas plus que de le lancer avec (bool). Si je fais

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

cela ne crée pas d'erreur, mais il ne lie pas mon modèle lorsqu'il est publié et foo est défini sur null. Quel est le meilleur moyen d'afficher Foo sur la page et de le lier à mon modèle sur un post?

84
DMulligan

Je l'ai avec pour travailler 

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

puis faire un Boolean.cshtml dans mon dossier EditorTemplates et coller 

@model bool?

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

à l'intérieur.

98
DMulligan

Réponse trouvée dans une question similaire - Rendre Nullable Bool sous la forme CheckBox . C'est très simple et ne fonctionne que:

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

C'est comme une réponse acceptée de @afinkelstein sauf que nous n'avons pas besoin d'un 'modèle d'éditeur' spécial

35
resnyanskiy

J'ai bool? IsDisabled { get; set; } dans le modèle. Inséré si dans la vue.

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

Mon modèle a un booléen qui doit être nullable

Pourquoi? Cela n'a pas de sens. Une case à cocher a deux états: cochée/décochée, ou vraie/fausse si vous voulez. Il n'y a pas de troisième état. 

Ou attendez-vous que vous utilisiez vos modèles de domaine dans vos vues au lieu de voir les modèles? C'est ton problème. La solution pour moi consiste donc à utiliser un modèle de vue dans lequel vous allez définir une propriété booléenne simple:

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

et maintenant, votre action de contrôleur transmettra ce modèle de vue à la vue et générera la case à cocher appropriée.

14
Darin Dimitrov

Compliquer une primitive avec des champs cachés pour préciser si False ou Null n'est pas recommandé.

La case à cocher n'est pas ce que vous devriez utiliser - elle n'a en réalité qu'un seul état: Cochée . Sinon, ça pourrait être n'importe quoi. 

Lorsque votre champ de base de données est un booléen nullable (bool?), l'UX doit utiliser les boutons 3-Radio, le premier bouton représentant votre "Vérifié", le deuxième bouton représentant "Non vérifié" et le troisième bouton représentant votre valeur nulle, quelle que soit la sémantique. de null signifie. Vous pouvez utiliser une liste déroulante <select><option> pour enregistrer des biens immobiliers, mais l'utilisateur doit cliquer deux fois et les choix ne sont pas aussi nets que instantanément.

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

RadioButtonList, défini comme une extension nommée RadioButtonForSelectList, construit les boutons radio pour vous, y compris la valeur sélectionnée/cochée, et définit le <div class="RBxxxx"> afin que vous puissiez utiliser css pour rendre vos boutons radio horizontaux (affichage: inline-block), vertical, ou à la manière d'une table (display: inline-block; width: 100px;)

Dans le modèle (j'utilise string, string pour la définition du dictionnaire comme exemple pédagogique. Vous pouvez utiliser 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());
}
}
6
Jules Bartow

L'approche la plus propre que je pourrais proposer consiste à étendre les extensions disponibles à HtmlHelper tout en réutilisant les fonctionnalités fournies par la structure.

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

J'ai essayé de «modeler» l'expression pour permettre un passage direct vers le CheckBoxFor<Expression<Func<T, bool>>> natif, mais je ne pense pas que ce soit possible.

3
Red Taz

Pour moi, la solution a été de changer le modèle de vue. Considérez que vous recherchez des factures. Ces factures peuvent être payées ou non. Votre recherche comporte donc trois options: payée, non payée ou "je ne m'en soucie pas".

Je l'avais réglé à l'origine comme un bool? champ:

public bool? PaidInvoices { get; set; }

Cela m'a fait trébucher sur cette question. J'ai fini par créer un type Enum et je l'ai traité comme suit:

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

Bien sûr, je les avais emballés dans des étiquettes et j'avais un texte spécifié, je veux dire voici une autre option à considérer.

3
Paul

La case à cocher ne vous propose que 2 valeurs (true, false). NULLable boolean a 3 valeurs (true, false, null) il est donc impossible de le faire avec une case à cocher.

Une bonne option consiste à utiliser un menu déroulant.

Modèle

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

Manette

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

Vue

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

En fait, je créerais un modèle et utiliserais ce modèle avec un EditorFor ().

Voici comment je l'ai fait:

  1. Create My template, qui est une vue partielle que j'ai créée dans le répertoire EditorTemplates, sous Partagé, sous Vues, ​​nommez-le ainsi (par exemple): 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. Utilisez-le comme ceci (dans n'importe quelle vue): 

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

C'est tout.

Les modèles sont si puissants dans MVC, utilisez-les .. Vous pouvez créer une page entière en tant que modèle, que vous utiliseriez avec la fonction @Html.EditorFor(); à condition que vous passiez son modèle de vue dans l'expression lambda ..

2
t_plusplus

J'ai eu un problème similaire dans le passé. 

Créez une entrée de case à cocher en HTML et définissez le nom d'attribut = "Foo". Cela devrait quand même être envoyé correctement. 

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

Lors de la création d'un EditorTemplate pour un modèle contenant un booléen nullable ...

  • Divisez le bool nullable en 2 booléens:

    // 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; }
    
  • Dans le modèle d'éditeur:

    @Html.HiddenFor(m => m.FooIsNull)
    
    @if (Model.FooIsNull)
    {
        // Null means "checkbox is hidden"
        @Html.HiddenFor(m => m.FooCheckbox)
    }
    else
    {
        @Html.CheckBoxFor(m => m.FooCheckbox)
    }
    
  • Ne publiez pas la propriété d'origine Foo, car elle est maintenant calculée à partir de FooIsNull et FooCheckbox.

0
user1023602

Méthodes d'extension:

        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
Jim

J'ai aussi fait face au même problème. J'ai essayé l'approche suivante pour résoudre le problème Parce que je ne veux pas changer la base de données et générer à nouveau l'EDMX.

@{

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

 }

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

Toutes les réponses ci-dessus sont venues avec ses propres problèmes. Le moyen le plus simple et le plus propre IMO est de créer un assistant

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

Utilisation

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

Dans mon scénario, une case nullable signifie qu'un membre du personnel n'a pas encore posé la question au client. Elle est donc entourée d'un .checkbox-nullable afin que vous puissiez styliser de manière appropriée et aider l'utilisateur final à déterminer qu'il ne s'agit ni de la variable true ni de la variable false.

CSS

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

Il suffit de vérifier la valeur null et de lui renvoyer false:

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