Il modello MVC richiede true


86

Esiste un modo attraverso le annotazioni dei dati per richiedere che una proprietà booleana sia impostata su true?

public class MyAwesomeObj{
    public bool ThisMustBeTrue{get;set;}
}

Qual è esattamente il caso d'uso di questo? Non potresti semplicemente lasciare che la proprietà sia di sola lettura e restituisca sempre true?
Jan Thomä

1
È praticamente da dire ... ehi amico, ti sei dimenticato di controllare il Sono d'accordo ... che dovrebbe rendere il modello non valido.
Marty Trenouth

Penso che questo sia qualcosa che vorresti gestire dal lato client.
PsychoCoder

15
@PsychoCoder: dovrebbe essere gestito da entrambi i lati ... non solo dal lato client. Stavo solo cercando di vedere se poteva essere gestito aggiungendo una semplice annotazione dei dati.
Marty Trenouth

Risposte:


49

Puoi creare il tuo validatore:

public class IsTrueAttribute : ValidationAttribute
{
    #region Overrides of ValidationAttribute

    /// <summary>
    /// Determines whether the specified value of the object is valid. 
    /// </summary>
    /// <returns>
    /// true if the specified value is valid; otherwise, false. 
    /// </returns>
    /// <param name="value">The value of the specified validation object on which the <see cref="T:System.ComponentModel.DataAnnotations.ValidationAttribute"/> is declared.
    ///                 </param>
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");

        return (bool) value;
    }

    #endregion
}

Considererei di migliorare questo con un'implementazione lato client - piuttosto che utilizzare la convalida remota a cui si fa riferimento in altre risposte, usa la discreta spiegazione
SamStephens

Questa è una buona (e testata) soluzione rapida per noi. Possiamo fare a meno della convalida lato client nella soluzione di @ dazbradbury (anche quella buona) perché ne abbiamo bisogno solo su una casella di controllo solitaria nella pagina precedente di un sondaggio.
Seth

return (bool) value == true;questo è un confronto ridondante
T-moty

130

Creerei un validatore sia per il lato server che per quello client. Utilizzando MVC e la convalida del modulo non invadente, ciò può essere ottenuto semplicemente effettuando le seguenti operazioni:

Innanzitutto, crea una classe nel tuo progetto per eseguire la convalida lato server in questo modo:

public class EnforceTrueAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value == true;
    }

    public override string FormatErrorMessage(string name)
    {
        return "The " + name + " field must be checked in order to continue.";
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = String.IsNullOrEmpty(ErrorMessage) ? FormatErrorMessage(metadata.DisplayName) : ErrorMessage,
            ValidationType = "enforcetrue"
        };
    }
}

Successivamente, annota la proprietà appropriata nel tuo modello:

[EnforceTrue(ErrorMessage=@"Error Message")]
public bool ThisMustBeTrue{ get; set; }

Infine, abilita la convalida lato client aggiungendo il seguente script alla tua vista:

<script type="text/javascript">
    jQuery.validator.addMethod("enforcetrue", function (value, element, param) {
        return element.checked;
    });
    jQuery.validator.unobtrusive.adapters.addBool("enforcetrue");
</script>

Nota: abbiamo già creato un metodo GetClientValidationRulesche invia la nostra annotazione alla vista dal nostro modello.

Se si utilizzano file di risorse per fornire il messaggio di errore per l'internazionalizzazione, rimuovere la FormatErrorMessagechiamata (o semplicemente chiamare la base) e modificare il GetClientValidationRulesmetodo in questo modo:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    string errorMessage = String.Empty;
    if(String.IsNullOrWhiteSpace(ErrorMessage))
    {
        // Check if they supplied an error message resource
        if(ErrorMessageResourceType != null && !String.IsNullOrWhiteSpace(ErrorMessageResourceName))
        {
            var resMan = new ResourceManager(ErrorMessageResourceType.FullName, ErrorMessageResourceType.Assembly);
            errorMessage = resMan.GetString(ErrorMessageResourceName);
        }
    }
    else
    {
        errorMessage = ErrorMessage;
    }

    yield return new ModelClientValidationRule
    {
        ErrorMessage = errorMessage,
        ValidationType = "enforcetrue"
    };
}

3
Grazie per questo - funziona benissimo! Funziona meglio con il metodo override FormatErrorMessage rimosso: in questo modo funziona la localizzazione dei messaggi di errore dai file di risorse. Il mio utilizzo: [EnforceTrue (ErrorMessageResourceType = typeof (ValidationMessages), ErrorMessageResourceName = "TermsAndConditionsRequired")]
Matt Frear

2
Non riesco a far funzionare la convalida lato client e non riesco a capire cosa sto facendo di sbagliato. Dove devo mettere esattamente il javacsript? Nell'etichetta della testa? Accanto al controller?
vsdev

Sono d'accordo, questa dovrebbe essere la risposta
Simua

1
Ottima soluzione che mostra la potenza degli attributi di convalida personalizzati! Sebbene io raccomandi di inserire lo script in un file js referenziato a livello globale, non le viste, per il riutilizzo. Inoltre, è meglio gestire tutti i modi in cui è possibile aggiungere le stringhe del messaggio: predefinito se non ne viene fornito nessuno, o la stringa del messaggio o da un file di risorse.
jeepwran

1
Ottima soluzione, grazie per la pubblicazione. Per coloro che non riescono a far funzionare la convalida lato client: è necessario estendere la convalida di jQuery prima che i controlli che convalideranno siano stati caricati, quindi mettere lo script in testa e non in window.onload / $ (document ) .ready () evento.
Evert

93

So che questo è un post precedente ma volevo condividere un semplice modo lato server per farlo. Si crea una proprietà pubblica impostata su true e si confronta il valore bool con quella proprietà. Se il tuo bool non è selezionato (per impostazione predefinita false) il modulo non verrà convalidato.

public bool isTrue
{ get { return true; } }

[Required]
[Display(Name = "I agree to the terms and conditions")]
[Compare("isTrue", ErrorMessage = "Please agree to Terms and Conditions")]
public bool AgreeTerms { get; set; }

Codice del rasoio

@Html.CheckBoxFor(m => Model.AgreeTerms, new { id = "AgreeTerms", @checked = "checked" })
<label asp-for="AgreeTerms" class="control-label"></label>
<a target="_blank" href="/Terms">Read</a>
<br />
@Html.ValidationMessageFor(model => model.AgreeTerms, "", new { @class = "text-danger" })
@Html.HiddenFor(x => Model.isTrue)

12
+1 per semplicità. FYI: ho dovuto rendere pubblica la proprietà "isTrue" affinché funzionasse.
Tod Birdsall

Il confronto non è lì per me in MVC4
Michael Rudner Evanchik

Super soluzione ottima soluzione
Sreerejith SS

9
E se aggiungi un nascosto per la proprietà "isTrue", ottieni la convalida lato client
billoreid

2
Fastidioso questa soluzione dall'aspetto fantastico non ha funzionato per me. Testato su Mvc 5.2.3.
harvzor

22

Ho provato diverse soluzioni ma nessuna di esse ha funzionato completamente per me per ottenere la convalida sia lato client che lato server. Quindi cosa ho fatto nella mia applicazione MVC 5 per farlo funzionare:

Nel tuo ViewModel (per la convalida lato server):

public bool IsTrue => true;

[Required]
[Display(Name = "I agree to the terms and conditions")]
[Compare(nameof(IsTrue), ErrorMessage = "Please agree to Terms and Conditions")]
public bool HasAcceptedTermsAndConditions { get; set; }

Nella tua pagina Razor (per la convalida lato client):

<div class="form-group">
   @Html.CheckBoxFor(m => m.HasAcceptedTermsAndConditions)
   @Html.LabelFor(m => m.HasAcceptedTermsAndConditions)
   @Html.ValidationMessageFor(m => m.HasAcceptedTermsAndConditions)

   @Html.Hidden(nameof(Model.IsTrue), "true")
</div>

1
Soluzione affascinante!
Tobias

3
Prenditi cura del valore per il campo nascosto ("true")!
Tobias

10

Vorrei solo indirizzare le persone al seguente Fiddle: https://dotnetfiddle.net/JbPh0X

L'utente ha aggiunto [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]alla sua proprietà booleana che fa funzionare la convalida lato server.

Per far funzionare anche la convalida lato client, hanno aggiunto il seguente script:

// extend jquery range validator to work for required checkboxes
var defaultRangeValidator = $.validator.methods.range;
$.validator.methods.range = function(value, element, param) {
    if(element.type === 'checkbox') {
        // if it's a checkbox return true if it is checked
        return element.checked;
    } else {
        // otherwise run the default validation function
        return defaultRangeValidator.call(this, value, element, param);
    }
}

9

Basta controllare per vedere se la sua rappresentazione di stringa è uguale a True:

[RegularExpression("True")]
public bool TermsAndConditions { get; set; }

@JeradRose È convalidato perfettamente sul server. Ti riferisci alla convalida lato client?
ta.speot

3
Confermato, funziona sul lato server ma non sul lato client
Matt Frear

Ho pensato che la convalida lato server potrebbe avere un'eccezione di mancata corrispondenza del tipo che tenta di confrontare un bool con una stringa.
Jerad Rose

RegularExpressionAttributeutilizza internamente Convert.ToStringper ottenere la rappresentazione di stringa del valore della proprietà (che viene consegnato ad esso come un object).
ta.speot

Penso che questa risposta sia più semplice di @ fields-cage +1 da me
Aaron Hudon

5

Puoi creare il tuo attributo o utilizzare CustomValidationAttribute .

Ecco come useresti CustomValidationAttribute:

[CustomValidation(typeof(BoolValidation), "ValidateBool")]

dove BoolValidation è definito come:

public class BoolValidation
{
  public static ValidationResult ValidateBool(bool boolToBeTrue)
  {
    if (boolToBeTrue)
    {
      return ValidationResult.Success;
    }
    else
    {
      return new ValidationResult(
          "Bool must be true.");
    }
  }

5

[Required]attributo sta per richiedere qualsiasi valore - può essere vero o falso. Dovresti usare un'altra convalida per quello.


3

Per ASP.NET Core MVC, ecco la convalida di client e server, basata sulla soluzione di dazbradbury

public class EnforceTrueAttribute : ValidationAttribute, IClientModelValidator
{
    public override bool IsValid(object value)
    {
        if (value == null) return false;
        if (value.GetType() != typeof(bool)) throw new InvalidOperationException("can only be used on boolean properties.");
        return (bool)value;
    }

    public void AddValidation(ClientModelValidationContext context)
    {
        MergeAttribute(context.Attributes, "data-val", "true");
        var errorMessage = ErrorMessage ?? 
            $"The value for field {context.ModelMetadata.GetDisplayName()} must be true.";
        MergeAttribute(context.Attributes, "data-val-enforcetrue", errorMessage);
    }

    private void MergeAttribute(IDictionary<string, string> attributes,
        string key,
        string value)
    {
        if (attributes.ContainsKey(key))
        {
            return;
        }
        attributes.Add(key, value);
    }
}

E poi sul client:

$.validator.addMethod("enforcetrue", function (value, element, param) {
    return element.checked;
});

$.validator.unobtrusive.adapters.addBool("enforcetrue");

Quindi l'utilizzo è:

[EnforceTrue(ErrorMessage = "Please tick the checkbox")]
public bool IsAccepted { get; set; }

Quando usi questa soluzione, considera questo . Metti il ​​codice javascript fuori da qualsiasi "jquery $document.ready()/ $(function() { });".
Igor

Un altro suggerimento, NON inserire l' requiredattributo nell'input HTML, come:<input asp-for="..." class="..." id="..." type="checkbox" required/>
Igor

3

Seguendo il post di ta.speot.is e il commento di Jerad Rose:

Il post fornito non funzionerà sul lato client con una convalida discreta. Questo dovrebbe funzionare in entrambi i campi (client e server):

[RegularExpression("(True|true)")]
public bool TermsAndConditions { get; set; }

Non so se si tratta di un problema di versione più recente, ma per me non funziona con jquery.validate 1.19.2 e jquery.validate.unobtrusive 3.2.11. Il problema sembra essere il regexmetodo discreto che definisce prima controlla se la casella di controllo è opzionale prima di convalidare la regex, il che ha senso, tranne che jquery.validate sembra considerare facoltativa qualsiasi casella di controllo non selezionata. tl; dr Esegue la regex solo sulle caselle di controllo selezionate. Possiamo aggiungere uno shim per il regex validatormetodo o semplicemente creare un validatore personalizzato.
xr280xr

3

.NET Core MVC: casella di controllo obbligatoria con annotazioni dei dati

public class MyModel
{
    [Display(Name = "Confirmation")]
    [Range(typeof(bool), "true", "true", ErrorMessage = "Please check the Confirmation checkbox.")]
    public bool IsConfirmed { get; set; }   
}

<div class="custom-control custom-checkbox col-10">
    <input type="checkbox" asp-for="IsConfirmed" class="custom-control-input" />
    <label class="custom-control-label" for="IsConfirmed">
        "By clicking 'submit', I confirm."
    </label>
    <span asp-validation-for="IsConfirmed" class="text-danger"></span>
</div>

<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>

<script type="text/javascript">
    $(document).ready(function () {
        // extend range validator method to treat checkboxes differently
        var defaultRangeValidator = $.validator.methods.range;
        $.validator.methods.range = function (value, element, param) {
            if (element.type === 'checkbox') {
                // if it's a checkbox return true if it is checked
                return element.checked;
            } else {
                // otherwise run the default validation function
                return defaultRangeValidator.call(this, value, element, param);
            }
        }
    });
</script>


2

Non conosco un modo per utilizzare DataAnnotations, ma questo è facilmente realizzabile nel tuo controller.

public ActionResult Add(Domain.Something model)
{

    if (!model.MyCheckBox)
        ModelState.AddModelError("MyCheckBox", "You forgot to click accept");

    if (ModelState.IsValid) {
        //'# do your stuff
    }

}

L'unica altra opzione sarebbe costruire un validatore personalizzato per il lato server e un validatore remoto per il lato client (la convalida remota è disponibile solo in MVC3 +)


Un po 'già nuovo come controllare il flag booleano già .... volevo sapere se c'era un'annotazione dati per esso.
Marty Trenouth

2

Hai gli elementi appropriati impostati nel web.config ?

Ciò potrebbe causare il mancato funzionamento della convalida.

Puoi anche provare a creare un attributo di convalida personalizzato (poiché [Required]si preoccupa solo se esiste o meno e ti interessa il valore):

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
sealed public class RequiredTrueAttribute : ValidationAttribute
{
    // Internal field to hold the mask value.
    readonly bool accepted;

    public bool Accepted
    {
        get { return accepted; }
    }

    public RequiredTrueAttribute(bool accepted)
    {
        this.accepted = accepted;
    }

    public override bool IsValid(object value)
    {
        bool isAccepted = (bool)value;
        return (isAccepted == true);
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentCulture,
          ErrorMessageString, name, this.Accepted);
    }
}

Quindi, utilizzo:

[RequiredTrue(ErrorMessage="{0} requires acceptance to continue.")]
public bool Agreement {get; set;}

Da qui .


2

Questo è ciò che ha funzionato per me. Nient'altro ha fatto. Mvc 5:

Modello

public string True
{
  get
  {
    return "true";
  }
}

[Required]
[Compare("True", ErrorMessage = "Please agree to the Acknowlegement")]
public bool Acknowlegement { get; set; }

Visualizza

  @Html.HiddenFor(m => m.True)
  @Html.EditorFor(model => model.Acknowlegement, new { htmlAttributes = Model.Attributes })
  @Html.ValidationMessageFor(model => model.Acknowlegement, "", new { @class = "text-danger" })

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine


1

Ho provato a usare la risposta di fields.cage e non ha funzionato per me, ma qualcosa di più semplice ha funzionato, e non sono sicuro esattamente perché (versione diversa di Razor, forse?), Ma tutto quello che dovevo fare era questo:

[Required]
[Range(typeof(bool), "true", "true", ErrorMessage = "Agreement required.")]
[Display(Name = "By clicking here, I agree that my firstborn child will etc etc...")]
public bool Agreement1Checked { get; set; }

E nel file .cshtml:

@Html.CheckBoxFor(m => m.Agreement1Checked)
@Html.LabelFor(m => m.Agreement1Checked)
@Html.ValidationMessageFor(m => m.Agreement1Checked)

Questo non funziona sul lato client per me. Per qualche motivo il parametro passato al metodo della regola jquery.validate è [NaN, NaN]dove dovrebbe essere[true, true]
xr280xr

@ xr280xr Anche quando l'utente ha selezionato la casella di controllo?
Dronz

0

Penso che il modo migliore per gestirlo sia semplicemente controllare nel tuo controller se la casella è vera, altrimenti aggiungi semplicemente un errore al tuo modello e visualizza di nuovo la tua vista.

Come affermato in precedenza, tutto ciò che [Obbligatorio] fa è assicurarsi che ci sia un valore e nel tuo caso, se non selezionato, ottieni comunque false.


0

Dai un'occhiata alla convalida infallibile qui . Puoi scaricarlo / installarlo tramite Nuget.

È una piccola libreria fantastica per questo genere di cose.


Ehhhh ... Gli attributi di convalida predefiniti funzionano abbastanza bene però.
Pangamma

0
/// <summary> 
///  Summary : -CheckBox for or input type check required validation is not working the root cause and solution as follows
///
///  Problem :
///  The key to this problem lies in interpretation of jQuery validation 'required' rule. I digged a little and find a specific code inside a jquery.validate.unobtrusive.js file:
///  adapters.add("required", function (options) {
///  if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
///    setValidationValues(options, "required", true);
///    }
///   });
///   
///  Fix: (Jquery script fix at page level added in to check box required area)
///  jQuery.validator.unobtrusive.adapters.add("brequired", function (options) {
///   if (options.element.tagName.toUpperCase() == "INPUT" && options.element.type.toUpperCase() == "CHECKBOX") {
///              options.rules["required"] = true;
///   if (options.message) {
///                   options.messages["required"] = options.message;
///                       }
///  Fix : (C# Code for MVC validation)
///  You can see it inherits from common RequiredAttribute. Moreover it implements IClientValidateable. This is to make assure that rule will be propagated to client side (jQuery validation) as well.
///  
///  Annotation example :
///   [BooleanRequired]
///   public bool iAgree { get; set' }
/// </summary>


public class BooleanRequired : RequiredAttribute, IClientValidatable
{

    public BooleanRequired()
    {
    }

    public override bool IsValid(object value)
    {
        return value != null && (bool)value == true;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        return new ModelClientValidationRule[] { new ModelClientValidationRule() { ValidationType = "brequired", ErrorMessage = this.ErrorMessage } };
    }
}

Sebbene questo collegamento possa rispondere alla domanda, è meglio includere le parti essenziali della risposta qui e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Ravi Dhoriya ツ

Funziona Controllare questo collegamento con il motivo per cui non riesce alla convalida- itmeze.com/2010/12/06/…
dhandapani harikrishnan

Oggi funziona. Puoi essere sicuro che continuerà a funzionare tra 5, 10 anni dopo? Questo database di domande e risposte è stato creato anche per i futuri utenti
Eliyahu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.