27

In a model of my ASP.NET MVC application I would like validate a textbox as required only if a specific checkbox is checked.

Something like

public bool retired {get, set};

[RequiredIf("retired",true)]
public string retirementAge {get, set};

How can I do that?

Thank you.

Castrohenge
  • 8,525
  • 5
  • 39
  • 66
Ahmet Dalyan
  • 271
  • 1
  • 3
  • 3
  • 2
    `[RequiredIf("retired == true")]`, [more here](https://github.com/JaroslawWaliszko/ExpressiveAnnotations) – jwaliszko Aug 13 '14 at 12:51

5 Answers5

15

Take a look at this: http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

I've modded the code somewhat to suit my needs. Perhaps you benefit from those changes as well.

public class RequiredIfAttribute : ValidationAttribute
{
    private RequiredAttribute innerAttribute = new RequiredAttribute();
    public string DependentUpon { get; set; }
    public object Value { get; set; }

    public RequiredIfAttribute(string dependentUpon, object value)
    {
        this.DependentUpon = dependentUpon;
        this.Value = value;
    }

    public RequiredIfAttribute(string dependentUpon)
    {
        this.DependentUpon = dependentUpon;
        this.Value = null;
    }

    public override bool IsValid(object value)
    {
        return innerAttribute.IsValid(value);
    }
}

public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
    public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
        : base(metadata, context, attribute)
    { }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        // no client validation - I might well blog about this soon!
        return base.GetClientValidationRules();
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        // get a reference to the property this validation depends upon
        var field = Metadata.ContainerType.GetProperty(Attribute.DependentUpon);

        if (field != null)
        {
            // get the value of the dependent property
            var value = field.GetValue(container, null);

            // compare the value against the target value
            if ((value != null && Attribute.Value == null) || (value != null && value.Equals(Attribute.Value)))
            {
                // match => means we should try validating this field
                if (!Attribute.IsValid(Metadata.Model))
                    // validation failed - return an error
                    yield return new ModelValidationResult { Message = ErrorMessage };
            }
        }
    }
}

Then use it:

public DateTime? DeptDateTime { get; set; }
[RequiredIf("DeptDateTime")]
public string DeptAirline { get; set; }
RickardN
  • 548
  • 4
  • 11
  • 3
    you need to add ' DataAnnotationsModelValidatorProvider.RegisterAdapter( typeof(RequiredIfAttribute), typeof(RequiredIfValidator));' to your global.asax.cs to get this to work. (as described in the link RickardN provided. – tkerwood Apr 13 '12 at 03:44
  • `ModelMetadata.FromLambdaExpression(ex, html.ViewData).IsRequired` is always returning `false` even thought the dependent upon property is true/false – Hanady Aug 21 '17 at 11:26
  • 1
    The `if` after `// compare the value against the target value` should instead read `(value == null && Attribute.Value == null)` as the first part of the condition. (change `!=` to `==`) – Cory-G Jul 09 '18 at 17:13
10

Just use the Foolproof validation library that is available on Codeplex: https://foolproof.codeplex.com/

It supports, amongst others, the following "requiredif" validation attributes / decorations:

[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]

To get started is easy:

  1. Download the package from the provided link
  2. Add a reference to the included .dll file
  3. Import the included javascript files
  4. Ensure that your views references the included javascript files from within its HTML for unobtrusive javascript and jquery validation.
  • Link? It seems to be working fine for me - I'm using EF 6. I downloaded the sourcecode for Foolproof and compiled it myself so perhaps that explains why it is working for me? –  Feb 24 '14 at 05:26
  • http://foolproof.codeplex.com/workitem/15609 and http://forums.asp.net/t/1752975.aspx – David Poxon Feb 24 '14 at 07:03
  • Can I use: [RequiredIfTrue] [RequiredIfFalse] - on single property with 2 different dependent properties?Example: [RequiredIfFalse("IsQuote", ErrorMessage = @"Provider must be entered.")] [RequiredIfTrue("IsIfa", ErrorMessage = @"Provider must be entered.")] [XmlElement("Provider", IsNullable = true)] public string Provider { get; set; } – Shivani Mar 25 '16 at 06:53
  • Most probably. My recommendation is to make the change and then run a unit test against it to see whether it works as intended. –  May 17 '16 at 09:15
6

Using NuGet Package Manager I intstalled this: https://github.com/jwaliszko/ExpressiveAnnotations

And this is my Model:

using ExpressiveAnnotations.Attributes;

public bool HasReferenceToNotIncludedFile { get; set; }

[RequiredIf("HasReferenceToNotIncludedFile == true", ErrorMessage = "RelevantAuditOpinionNumbers are required.")]
public string RelevantAuditOpinionNumbers { get; set; }

I guarantee you this will work!

5

I have not seen anything out of the box that would allow you to do this.

I've created a class for you to use, it's a bit rough and definitely not flexible.. but I think it may solve your current problem. Or at least put you on the right track.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace System.ComponentModel.DataAnnotations
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class RequiredIfAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' is required";
        private readonly object _typeId = new object();

        private string  _requiredProperty;
        private string  _targetProperty;
        private bool    _targetPropertyCondition;

        public RequiredIfAttribute(string requiredProperty, string targetProperty, bool targetPropertyCondition)
            : base(_defaultErrorMessage)
        {
            this._requiredProperty          = requiredProperty;
            this._targetProperty            = targetProperty;
            this._targetPropertyCondition   = targetPropertyCondition;
        }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, _requiredProperty, _targetProperty, _targetPropertyCondition);
        }

        public override bool IsValid(object value)
        {
            bool result             = false;
            bool propertyRequired   = false; // Flag to check if the required property is required.

            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            string requiredPropertyValue            = (string) properties.Find(_requiredProperty, true).GetValue(value);
            bool targetPropertyValue                = (bool) properties.Find(_targetProperty, true).GetValue(value);

            if (targetPropertyValue == _targetPropertyCondition)
            {
                propertyRequired = true;
            }

            if (propertyRequired)
            {
                //check the required property value is not null
                if (requiredPropertyValue != null)
                {
                    result = true;
                }
            }
            else
            {
                //property is not required
                result = true;
            }

            return result;
        }
    }
}

Above your Model class, you should just need to add:

[RequiredIf("retirementAge", "retired", true)]
public class MyModel

In your View

<%= Html.ValidationSummary() %> 

Should show the error message whenever the retired property is true and the required property is empty.

Hope this helps.

Zack
  • 598
  • 3
  • 8
  • 23
0

Try my custom validation attribute:

[ConditionalRequired("retired==true")]
public string retirementAge {get, set};

It supports multiple conditions.

karaxuna
  • 26,752
  • 13
  • 82
  • 117
  • 1
    Would be nice if you could provide a complete project in your repo, that includes all the required references like System.Linq.Dynamic. Furthermore you seem to be using a custom version of System.Linq.Dynamic as the Microsoft one has an `ExpressionParser.Parse()` method that takes 1 argument, but you are calling a 2-argument version. – Ian Kemp Nov 21 '13 at 10:13
  • Thanks @IanKemp, I'll consider your suggestions and improve repository – karaxuna Nov 21 '13 at 16:54
  • @karaxuna What do I do with ExpressionParser there is an error showing me on that? – 3 rules Nov 23 '16 at 11:20