I am using FluentValidation in my Asp.Net MVC 4 application. I have already known that some rules are automatically generating attributes for jQuery validation library. And this script library has already known what it must check for example in case of data-rule-required
, data-rule-range
and so on.
I know that there are some functions in FluentValidation, but these are not include for client-side. For example: .Equal(true)
.
I have checked @DarinDimitrov answer here and implemented this without any problem.
But, I don't want always to create new class which is inherited from FluentValidationPropertyValidator
. And we must add this to provider like that in global.asax:
provider.Add(typeof(EqualValidator), (metadata, context, description, validator) => new EqualToValueClientRule(metadata, context, description, validator));
In this case EqualValidator
already implemented in FluentValidation. But, what if we have created a validator with When
keyword. For example, I have:
this.RuleFor(phone => phone.Digits)
.Length(7)
.When(phone => phone.PrefixId == 2)
.WithMessage("Numbers in 2nd city must contain 7 characters");
this.RuleFor(phone => phone.Digits)
.Length(7)
.When(phone => phone.PrefixId > 64)
.WithMessage("Mobile number must contain 7 characters");
this.RuleFor(phone => phone.Digits)
.Length(5)
.When(phone => phone.PrefixId != 2)
.WithMessage("Numbers in other cities must contain 5 characters")
Of course, I can check this with jQuery/JavaScript without any problem. But, this approach is not good. And in other cases you have to write so much code for generating custom attributes in client side and add new function to adapter. Or, just use jQuery/JavaScript? Or any other thing? May be we can add JavaScript function name to FluentValidationPropertyValidator
?
What do you recommend me?
I have thought a lot and find that the best way is to create new validator which inherits from PropertyValidator
and implements IClientValidatable
interface. As a result, it will contain server-side validation and will generate unobtrusive attributes as we wish. Then we must need register this new validator in unobtrusive library.
For example, the validator for the rules in my question will be:
public class MustFitToPhonePrefix<TModel, TProperty> : PropertyValidator, IClientValidatable
{
private string dependencyElement;
public MustFitToPhonePrefix(Expression<Func<TModel, TProperty>> expression)
: base("Format is wrong")
{
dependencyElement = (expression.Body as MemberExpression).Member.Name;
}
// Server side validation
protected override bool IsValid(PropertyValidatorContext context)
{
// Instance of the class which contains property which must be validated
var phone = context.ParentContext.InstanceToValidate as PhoneDetail;
...
// Custom logic
...
// Everything is valid
return true;
}
// Generate jquery unobtrusive attributes
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessageSource.GetString(), // default error message
ValidationType = "fittoprefix" // name of the validatoin which will be used inside unobtrusive library
};
rule.ValidationParameters["prefixelement"] = dependencyElement; // html element which includes prefix information
yield return rule;
}
And now we can register our client-side validator:
// Will check if the phone number fits to phone prefix
$.validator.addMethod('fittoprefix', function (value, element, params) {
var parent = getParentPropertyName(element);
var prefixId = $("#{0}_{1}".format(parent, params.prefixelement)).val();
var digitsLength = $(element).val().Length;
...
// Custom logic
...
return true;
});
// Registration - Will check if the phone number fits to phone prefix
$.validator.unobtrusive.adapters.add('fittoprefix', ['prefixelement'], function (options) {
options.rules['fittoprefix'] = options.params;
if (options.message != null) {
options.messages['fittoprefix'] = options.message;
}
});
And at last, we can set our validator:
this.RuleFor(m => m.Digits)
.SetValidator(new MustFitToPhonePrefix<PhoneDetail, int>(m => m.PrefixId));