28

Simple problem here (I think).

I have a form with a checkbox at the bottom where the user must agree to the terms and conditions. If the user doesn't check the box, I'd like an error message to be displayed in my validation summary along with the other form errors.

I added this to my view model:

[Required]
[Range(1, 1, ErrorMessage = "You must agree to the Terms and Conditions")]
public bool AgreeTerms { get; set; }

But that didn't work.

Is there an easy way to force a value to be true with data annotations?

Steven
  • 18,761
  • 70
  • 194
  • 296
  • Custom annotations are very easy to write, have you considered that option? – asawyer Aug 08 '11 at 18:44
  • Not for a bool, but very similar (and allows for custom error messages): http://rical.blogspot.com/2012/03/server-side-custom-annotation.html – pkr Nov 14 '13 at 17:54

4 Answers4

28
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace Checked.Entitites
{
    public class BooleanRequiredAttribute : ValidationAttribute, IClientValidatable
    {
        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 = "booleanrequired", ErrorMessage = this.ErrorMessage } };
            yield return new ModelClientValidationRule() 
            { 
                ValidationType = "booleanrequired", 
                ErrorMessage = this.ErrorMessageString 
            };
        }
    }
}
Richard
  • 14,798
  • 21
  • 70
  • 103
Ryan Gross
  • 6,423
  • 2
  • 32
  • 44
8

There's actually a way to make it work with DataAnnotations. The following way:

    [Required]
    [Range(typeof(bool), "true", "true")]
    public bool AcceptTerms { get; set; }
Ruben Szekér
  • 1,065
  • 1
  • 10
  • 21
  • This didn't work for me, since the clientside validation fired both on true and false. – Peter Feb 24 '20 at 09:22
  • 1
    Works great, and specify the error message to override the default range message: `[Required, Range(typeof(bool), "true", "true", ErrorMessage = "Accepting terms is required")]` – Subjective Reality Feb 25 '22 at 07:40
7

You can write a custom validation attribute which has already been mentioned. You will need to write custom javascript to enable the unobtrusive validation functionality to pick it up if you are doing client side validation. e.g. if you are using jQuery:

// extend jquery unobtrusive validation
(function ($) {

  // add the validator for the boolean attribute
  $.validator.addMethod(
    "booleanrequired",
    function (value, element, params) {

      // value: the value entered into the input
      // element: the element being validated
      // params: the parameters specified in the unobtrusive adapter

      // do your validation here an return true or false

    });

  // you then need to hook the custom validation attribute into the MS unobtrusive validators
  $.validator.unobtrusive.adapters.add(
    "booleanrequired", // adapter name
    ["booleanrequired"], // the names for the properties on the object that will be passed to the validator method
    function(options) {

      // set the properties for the validator method
      options.rules["booleanRequired"] = options.params;

      // set the message to output if validation fails
      options.messages["booleanRequired] = options.message;

    });

} (jQuery));

Another way (which is a bit of a hack and I don't like it) is to have a property on your model that is always set to true, then use the CompareAttribute to compare the value of your *AgreeTerms * attribute. Simple yes but I don't like it :)

Si Kenyon
  • 71
  • 3
3

ASP.Net Core 3.1

I know this is a very old question but for asp.net core the IClientValidatable does not exist and i wanted a solution that works with jQuery Unobtrusive Validation as well as on server validation so with the help of this SO question Link i made a small modification that works with boolean field like checkboxes.

Attribute Code

   [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class MustBeTrueAttribute : ValidationAttribute, IClientModelValidator
    {
        public void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            var errorMsg = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
            MergeAttribute(context.Attributes, "data-val-mustbetrue", errorMsg);
        }

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

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

    }

Model

    [Display(Name = "Privacy policy")]
    [MustBeTrue(ErrorMessage = "Please accept our privacy policy!")]
    public bool PrivacyPolicy { get; set; }

Client Side Code

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

$.validator.unobtrusive.adapters.add("mustbetrue", [], function (options) {
    options.rules.mustbetrue = {};
    options.messages["mustbetrue"] = options.message;
});
Community
  • 1
  • 1
HMZ
  • 2,949
  • 1
  • 19
  • 30