21

Is there a way to make a data annotation conditional? I have a table Party where I store both organisations and persons. If I'm adding an organisation I don't want the field surname to be required, but only if I'm adding a person.

public class Party
{
    [Required(ErrorMessage = "{0} is missing")]
    [DisplayName("Your surname")]
    public object surname { get; set; }

    [DisplayName("Type")]
    public object party_type { get; set; }
    ...
}  

I'd like a condition for the required data annotation of surname, something like:
if (party_type=='P') then surname is required, else the surname can be empty.

EDIT
If I have to move this validation to the controller, how would I do it there? How can I trigger the same error message from there?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Niklas
  • 13,005
  • 23
  • 79
  • 119

3 Answers3

31

You can make your model inherit from IValidatableObject and then put your custom logic into the Validate method. You'll have to remove the RequredAttribute from the property as well. You will have to write some custom javascript to validate this rule on the client as the Validate method doesn't translate into the unobtrusive validation framework. Note I changed your properties to strings to avoid casting.

Also, if you have other validation errors from attributes, those will fire first and prevent the Validate method from being run so you only detect these errors if the attribute-based validation is ok.

public class Party : IValidatableObject
{
    [DisplayName("Your surname")]
    public string surname { get; set; }

    [DisplayName("Type")]
    public string party_type { get; set; }
    ...

    public IEnumerable<ValidationResult> Validate( ValidationContext context )
    {
         if (party_type == "P" && string.IsNullOrWhitespace(surname))
         {
              yield return new ValidationResult("Surname is required unless the party is for an organization" );
         }
    }
}

On the client you can do something like:

 <script type="text/javascript">
 $(function() {
      var validator = $('form').validate();
      validator.rules('add', {
          'surname': {
              required: {
                 depends: function(element) {
                      return $('[name=party_type]').val() == 'P';
                 }
              },
              messages: {
                  required: 'Surname is required unless the party is for an organization.'
              }
           }
      });
 });
 </script>
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • My party class is generated from a dbml so I don't want to change in that part of my code. Can I still do this if I create a partial class? – Niklas Jun 23 '11 at 12:12
  • @Niklas - yes. Just have the partial you create inherit from IValidatableObject and implement the Validate method. If you're stuck with object as the type (seems odd, isn't the column a character type?), you'll have to use casting in the Validate method. The IValidatableObject is tied into the model binding validation logic so you can simply check for a valid model in the controller. Note that if you have other validation errors from attributes, those will fire first and prevent the Validate method from being run so you only detect these errors if the attribute-based validation is ok. – tvanfosson Jun 23 '11 at 12:18
  • Yeah, it's a string in my generated class. I copied from my Party_Metadata class where I keep my annotations. I guess it have to be object there in case I change the type in the DB? (just guessing). Thank you, I'll try this. – Niklas Jun 23 '11 at 12:23
  • Ok, last one. If you use data annotations not only do you get a summary message but the input field of surname turns red too. Can I target the input field of surname somehow? – Niklas Jun 23 '11 at 13:14
  • 1
    @Niklas - if you use the constructor that takes a list of property names on ValidationResult you can specify which model properties are associated with the message. I presume that this would work the same way and add the validation errors to those properties as well as to the summary. That should trigger the validation logic for that property in the view. – tvanfosson Jun 23 '11 at 13:59
  • Note that `System.Web.Mvc.IClientValidatable` can now be implemented to register rules and javascript methods to execute when validating those rules. – ps2goat Jul 23 '14 at 15:38
3

I know this topic has some time, but if you'd like to use only declarative validation for that, you could just use such a simple construction (see this reference for further possibilities):

[RequiredIf(DependentProperty = "party_type", TargetValue = "P")]
public string surname { get; set; }

public string party_type { get; set; }

Update:

Since the ExpressiveAnnotations 2.0, there is a breaking change. Now the same thing can be done in a simpler manner:

[RequiredIf("party_type == 'P'")]
public string surname { get; set; }
jwaliszko
  • 16,942
  • 22
  • 92
  • 158
  • 1
    I can't implement this since it's been a while that I worked on the project, but if it works it sounds a lot easier. – Niklas Dec 10 '13 at 12:22
0

In Controller you can check like this: Before if (ModelState.IsValid)

if (model.party_type == 'p')
{
   this.ModelState.Remove("surname");
}
Nalan Madheswaran
  • 10,136
  • 1
  • 57
  • 42