El modelo MVC requiere verdadero


85

¿Hay alguna manera de que las anotaciones de datos requieran que una propiedad booleana se establezca en verdadera?

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

¿Cuál es exactamente el caso de uso de esto? ¿No podría dejar que la propiedad sea de solo lectura y vuelva a ser verdadera todo el tiempo?
Jan Thomä

1
Es más o menos para decir ... hola amigo, se olvidó de marcar el Acepto ... lo que debería invalidar el modelo.
Marty Trenouth

Creo que esto es algo que le gustaría manejar del lado del cliente.
PsychoCoder

15
@PsychoCoder: Debe manejarse en ambos lados ... no solo en el lado del cliente. Solo estaba buscando para ver si se podía manejar agregando una anotación de datos simple.
Marty Trenouth

Respuestas:


49

Podrías crear tu propio validador:

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
}

Consideraría mejorar esto con una implementación del lado del cliente; en lugar de usar la validación remota a la que se hace referencia en otras respuestas, use el discreto que se detalla
SamStephens

Esta es una buena (y probada) solución rápida para nosotros. Podemos prescindir de la validación del lado del cliente en la solución de @ dazbradbury (también una buena) porque solo necesitamos esto en una casilla de verificación solitaria en la página anterior de una encuesta.
Seth

return (bool) value == true;esta es una comparación redundante
T-moty

130

Crearía un validador tanto para el servidor como para el cliente. Usando MVC y la validación de formularios discretos, esto se puede lograr simplemente haciendo lo siguiente:

En primer lugar, cree una clase en su proyecto para realizar la validación del lado del servidor así:

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

Después de esto, anote la propiedad apropiada en su modelo:

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

Y finalmente, habilite la validación del lado del cliente agregando el siguiente script a su Vista:

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

Nota: Ya creamos un método GetClientValidationRulesque empuja nuestra anotación a la vista desde nuestro modelo.

Si usa archivos de recursos para proporcionar el mensaje de error para la internacionalización, elimine la FormatErrorMessagellamada (o simplemente llame a la base) y modifique el GetClientValidationRulesmétodo así:

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
Gracias por esto, ¡funciona muy bien! Funciona mejor con el método de anulación FormatErrorMessage eliminado, de esa manera funciona la localización de los mensajes de error de los archivos de recursos. Mi uso: [EnforceTrue (ErrorMessageResourceType = typeof (ValidationMessages), ErrorMessageResourceName = "TermsAndConditionsRequired")]
Matt Frear

2
No puedo hacer que la validación del lado del cliente funcione y parece que no puedo decir qué estoy haciendo mal. ¿Dónde debería poner exactamente el javacsript? ¿En la etiqueta de la cabeza? ¿Junto al controlador?
vsdev

Estoy de acuerdo, esta debería ser la respuesta
Simua

1
¡Gran solución que muestra el poder de los atributos de validación personalizados! Aunque recomiendo poner el script en un archivo js referenciado globalmente, no en las vistas, para su reutilización. Además, es mejor manejar todas las formas en que se pueden agregar las cadenas de mensajes: predeterminado si no se proporciona ninguna, o la cadena del mensaje, o desde un archivo de recursos.
jeepwran

1
Gran solución, gracias por publicar. Para aquellos que no pueden hacer que la validación del lado del cliente funcione: debe extender la validación de jQuery antes de que se carguen los controles que validará, así que coloque el script en el encabezado y no en la ventana .onload / $ (document ) .Ready () evento.
Evert

92

Sé que esta es una publicación anterior, pero quería compartir una forma simple del lado del servidor para hacer esto. Usted crea una propiedad pública establecida en true y compara su bool con esa propiedad. Si su bool no está marcado (por defecto es falso), el formulario no se validará.

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

Código de la maquinilla de afeitar

@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 por simplicidad. FYI: Tuve que hacer pública la propiedad 'isTrue' para que esto funcione.
Tod Birdsall

Compare no está ahí para mí en MVC4
Michael Rudner Evanchik

Súper solución gran solución
Sreerejith SS

9
Y si agrega un oculto para la propiedad "isTrue", obtiene la validación del lado del cliente
billoreid

2
Molestar esta excelente solución no funcionó para mí. Probado en Mvc 5.2.3.
harvzor

22

Probé varias soluciones, pero ninguna funcionó completamente para mí para obtener la validación del lado del cliente y del servidor. Entonces, lo que hice en mi aplicación MVC 5 para que funcione:

En su ViewModel (para la validación del lado del servidor):

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

En su página de Razor (para la validación del lado del cliente):

<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
¡Solución encantadora!
Tobias

3
¡Tenga cuidado con el valor del campo oculto ("verdadero")!
Tobias

10

Solo me gustaría dirigir a las personas al siguiente Fiddle: https://dotnetfiddle.net/JbPh0X

El usuario agregó [Range(typeof(bool), "true", "true", ErrorMessage = "You gotta tick the box!")]a su propiedad booleana que hace que funcione la validación del lado del servidor.

Para que también funcione la validación del lado del cliente, agregaron el siguiente 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

Simplemente verifique si su representación de cadena es igual a True:

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

@JeradRose Está validado muy bien en el servidor. ¿Se refiere a la validación del lado del cliente?
ta.speot.is

3
Confirmado, esto funciona del lado del servidor pero no del lado del cliente
Matt Frear

Pensé que la validación del lado del servidor podría tener una excepción de falta de coincidencia de tipos al intentar comparar un bool con una cadena.
Jerad Rose

RegularExpressionAttributeutiliza internamente Convert.ToStringpara obtener la representación de cadena del valor de la propiedad (que se le entrega como un object).
ta.speot.is

Creo que esta respuesta es más simple que @ fields-cage +1 de mi parte
Aaron Hudon

5

Puede crear su propio atributo o utilizar CustomValidationAttribute .

Así es como usaría CustomValidationAttribute:

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

donde BoolValidation se define como:

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

5

[Required]atributo significa que requiere cualquier valor, puede ser verdadero o falso. Tendría que usar otra validación para eso.


3

Continuando con la publicación de ta.speot.is y el comentario de Jerad Rose:

La publicación dada no funcionará del lado del cliente con una validación discreta. Esto debería funcionar en ambos campos (cliente y servidor):

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

No sé si se trata de un problema de versión más reciente, pero no me funciona con jquery.validate 1.19.2 y jquery.validate.unobtrusive 3.2.11. El problema parece ser que el regexmétodo discreto define primero verifica si la casilla de verificación es opcional antes de validar la expresión regular, lo cual tiene sentido, excepto que jquery.validate parece considerar cualquier casilla de verificación no marcada como opcional. tl; dr Solo ejecuta la expresión regular en las casillas de verificación marcadas. Podemos agregar una corrección para el regex validatormétodo o simplemente crear un validador personalizado.
xr280xr

3

.NET Core MVC: casilla de verificación obligatoria con anotaciones de datos

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

No conozco una forma de realizar anotaciones de datos, pero esto se hace fácilmente en su controlador.

public ActionResult Add(Domain.Something model)
{

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

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

}

La única otra opción sería construir un validador personalizado para el lado del servidor y un validador remoto para el lado del cliente (la validación remota solo está disponible en MVC3 +)


Ya es un poco nuevo cómo verificar la bandera booleana ... quería saber si había una anotación de datos para ello.
Marty Trenouth

2

¿Tiene los elementos adecuados configurados en web.config ?

Eso podría hacer que la validación no funcione.

También puede intentar crear un atributo de validación personalizado (ya que [Required]solo le importa si existe o no, y a usted le importa el valor):

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

Entonces, uso:

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

De aqui .


2

Esto es lo que funcionó para mí. Nada más hizo. MVC 5:

Modelo

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

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

Ver

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

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


2

Para ASP.NET Core MVC, aquí está la validación de cliente y servidor, basada en la solución de 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);
    }
}

Y luego sobre el cliente:

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

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

Entonces el uso es:

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

1

Intenté usar la respuesta de fields.cage y no funcionó del todo para mí, pero algo más simple sí lo hizo, y no estoy seguro exactamente por qué (¿una versión diferente de Razor, tal vez?), Pero todo lo que tenía que hacer era esto:

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

Y en el archivo .cshtml:

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

Esto no funciona del lado del cliente para mí. Por alguna razón, el parámetro pasado al método de regla jquery.validate es [NaN, NaN]donde debería estar[true, true]
xr280xr

@ xr280xr ¿Incluso cuando el usuario ha marcado la casilla de verificación?
Dronz

0

Creo que la mejor manera de manejar esto es simplemente verificar en su controlador si la casilla es verdadera; de lo contrario, simplemente agregue un error a su modelo y haga que vuelva a mostrar su vista.

Como se indicó anteriormente, lo único que hace [Obligatorio] es asegurarse de que haya un valor y, en su caso, si no se marca, seguirá siendo falso.


0

Echa un vistazo a la validación infalible aquí . Puede descargarlo / instalarlo a través de Nuget.

Es una gran biblioteca pequeña para este tipo de cosas.


Ehhhh ... Sin embargo, los atributos de validación predeterminados funcionan bastante bien.
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 } };
    }
}

Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada.
Ravi Dhoriya ツ

Funciona Consulte este enlace con la razón por la que falla en la validación- itmeze.com/2010/12/06/…
dhandapani harikrishnan

Hoy funciona. ¿Puede estar seguro de que seguirá funcionando en 5 o 10 años después? Estas bases de datos de preguntas y respuestas también se crean para futuros usuarios
Eliyahu
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.