DotVVM:Build Conditional Validation Attribute
Tags: dotnet, dotvvm, csharp
Published: 2020-04-26 01:32:28

#### Validation in DotVVM 
Validation in DotVVM is very similar to validation in ASP.Net MVC and ASP.Net Core.
You can validate your view model by using validation attributes. DotVVM do client side validation for only the following attributes
- Required
- Compare
- EmailAddress
- Range
- RegularExporession

If the user input is not valid you have two controls you can use to show error messages
- _Validator_ Control : shows error message against the target input control
- _VaicationSummary_ Control: shows the summary for all errors in your view model. 

#### Straight Validation
Suppose we have a small registaration form that requires the user to enter his/her name, gender, and age. All fields are required. To validate user's input we just need to annotate our viewmodel properties with _Required_ attribute. Our ViewModel class will be like 

````
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }

public string Gender { get; set; }

[Required(ErrorMessage = "Age is required")]
public int? Age { get; set; }
```
In our view we will use only _Validator_ control to show error messages.

````
<dot:Literal class="control-label col-md-3" Text="Name"></dot:Literal>
<dot:TextBox Text="{value: Name}" class="form-control" Type="Normal"></dot:TextBox>
<dot:Validator HideWhenValid="true" Value="{value: Name}" ShowErrorMessageText="true"></dot:Validator>
<br /><br />
<dot:Literal class="control-label col-md-3" Text="Gender"></dot:Literal>
<dot:RadioButton CheckedItem="{value: Gender}" CheckedValue="{value: "1"}" Text="Male"></dot:RadioButton>
<dot:RadioButton CheckedItem="{value: Gender}" CheckedValue="{value: "2"}" Text="Female"></dot:RadioButton>
<br /><br />
<dot:Literal class="control-label col-md-3" Text="Age"></dot:Literal>
<dot:TextBox Text="{value: Age}" class="form-control" Type="Number"></dot:TextBox>
<dot:Validator HideWhenValid="true" Value="{value: Age}" ShowErrorMessageText="true"></dot:Validator>
<br /><br />
<dot:Button Click="{command: Save()}" Text="Save" class="btn btn-outline-secondary btn-sm"></dot:Button>
```
Foucs on _validator_ control we set 
- _HideWhenValid_ to _true_ to show error message **only** if the user's input is not valid
- _ShowErrorMessageText_ to true to show error message that comes from our ViewModel 

#### Conditional Validation
We know women don't like to tell their real age so we want to respect this in our registeration form by validating the age feild only if the user selected male as  his gender but if gender was female will not force her to enter her age.

In DotVVM there is no conditional Required validation, so we need to build our custom validation attribute.
What we actually need is just a new class that inherts _ValidationAttribute_ class and override _IsValid_ method with our  logic. 
````
public class RequiredIfAttribute : ValidationAttribute
{
    public string PropertyName { get; set; }
    public object Value { get; set; }

    public RequiredIfAttribute(string propertyName, object value, string errorMessage = "")
    {
        PropertyName = propertyName;
        ErrorMessage = errorMessage;
        Value = value;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var instance = validationContext.ObjectInstance;
        var type = instance.GetType();
        var proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == Value.ToString() && value == null)
        {
            return new ValidationResult(ErrorMessage);
        }
        return ValidationResult.Success;
    }
}
```
First we added a _PropertyName_ property to check its value to start validation of skip it and _Value_ property that will check against it.
Inside _IsValid()_ method we are using reflection to get that value of the property and check if its value is equal to the provided value in the attribute  and the value of the current field is null then the field value is not valid otherwise the field is not required.

In our _ViewModel_ we will replace _Required_ attribute that decorate _Age_ property with our new custom attribute _RequiredIf_.

````
 [RequiredIf(nameof(Gender), "1", ErrorMessage = "Age is required")]
 public int? Age { get; set; }
```
Frist parameter is the _PropertyName_ and second paramter is the _Value_ of the _Gender_ that makes _Age_ field is required (I used 1 and 2 for simpilcity).

#### Restriction
Our custom validation is server side validation. DotVVM need you to submit your form to be able to run your custom validation.

#### Summary
In this post we validated our ViewModel like we used to do in  ASP.Net MVC or ASP.Net Core and also created our custom validation to validate fields based on another field value.
You can find the full source code on [github](https://github.com/miroprocessor/dotvvm-requiredIf).