56

Following on from this post Perform client side validation for custom attribute

I am trying to get my head around how to do this, passing additional parameters to the client-side script

As I understand it so far to implement custom validation with MVC 3 the following is required

Create a custom validation attribute

Based on ValidationAttribute and implementing IClientValidatable. I have also see some examples deriving from ModelValidator, which seems to implement the functionality of both ValidationAttribute and IClientValidatable. So this is my first point of confusion as to what the diffirences are or whether ModelValidator was used in MVC 2 but is now deprecated or what ?

An instance of ModelClientValidationRule must be returned from GetClientValidationRules() to specify details such as the error message, ValidationType (which I understand to be the name of the Javascript function that will perform the client-side validation) and any additional custom parameters that the attribute may have, and that need to be passed to the Javascript validation.

I assume that the runtime (not sure which part of it) then use the ModelClientValidationRule to generate html attribute in the tag elements as follows:

data-val="true"  (to indicate that the element requires validation)
data-val-[ValidationType]=[ErrorMessage]
data-val-[ValidationType].[ValidationParameters(n).Key]=[ValidationParameters(n).Value]

Implement the client-side validation logic

A Javascript function must be created and added to jQuery.validators with jQuery.validators.addmethod() so that JQuery is aware of it when it need to be executed. Something like:

jQuery.validator.addMethod(
    'greaterThan', 
    function (value, element, params) {
        /.../
       return /* true or false   */ ; 
    },
    ''
); 

My question here is whether the signature 'function (value, element, params)' is standard for methods that will handle validation and I assume it will be called by some jQuery functionality at the appropriate time such as before a form is submitted or when an element looses fuces or on keyUp events. I just don't undertand how you can controll this i.e. choose which event is appropriete for yout custom validation.

Implement an unobtrusive adapter

This translates unobtrusive attributes to; something I am not very clear on, but assume it to be a jQuery Rule, but I am not clear on how those work. Something like

jQuery.validator.unobtrusive.adapters.add(
    'futuredate', 
    { },
    function (options) {
        options.rules['greaterThan'] = true;
        options.messages['greaterThan'] = options.message;
    }
); 

My question here is about 'function (options)'. Is this the function that will be called before 'function (value, element, params)' and is responsible for extracting the unobtrusive tags into a data structure that can be understood by jQuery.Validation. From the code example it seems to me that options is an object that contains both, the attribute values from the tag (such as options.message) and the jQuery relevant properties it must map to (such as options.messages['ClientSideValidationFunctionName']. If so how are custom parameters retrieved and mapped.

I hope I have not added any additional confusion.

Community
  • 1
  • 1
ricardo
  • 1,452
  • 1
  • 22
  • 24
  • 1
    Unobtrusive validation means HTML attributes are added on the element such as data-max-value="5" and javascript outside the body content looks for these and performs the validation, rather than adding javascript directly on the HTML element. – Rich Jan 24 '11 at 17:23

1 Answers1

70

You could use the ValidationParameters property to add custom parameters to the rule:

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    var rule = new ModelClientValidationRule
    {
        ErrorMessage = this.ErrorMessage,
        ValidationType = "futuredate",
    };
    rule.ValidationParameters.Add("param1", "value1");
    rule.ValidationParameters.Add("param2", "value2");
    yield return rule;
}

which could be used in the adapter:

jQuery.validator.unobtrusive.adapters.add(
    'futuredate', 
    [ 'param1', 'param2' ],
    function (options) {
        var param1 = options.params.param1; // shall equal 'value1'
        var param2 = options.params.param2; // shall equal 'value2'
        // TODO: use those custom parameters to define the client rules
    }
);

UPDATE:

As requested in the comments section here's how you could pass those parameters to the custom validator rule function:

jQuery.validator.unobtrusive.adapters.add(
    'futuredate', 
    [ 'param1', 'param2' ],
    function (options) {
        // simply pass the options.params here
        options.rules['greaterThan'] = options.params;
        options.messages['greaterThan'] = options.message;
    }
);

jQuery.validator.addMethod('greaterThan', function (value, element, params) {
    // params here will equal { param1: 'value1', param2: 'value2' }
    return ...
}, '');
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • ok great. Could you also please show how I would reference those params in the script that would perform the validation. This is where I am now stuck. I understand the method signature for the validation is function (value, element, params), but am not sure how param1 and param2 are passed to it – ricardo Jan 24 '11 at 17:35
  • P.S. I have register the validationmethod with jQuery.validator.addMethod('validationtype', function (value, element, params) { /* ...*/ },''); – ricardo Jan 24 '11 at 17:38
  • @ricardo, does this answer your question? – Darin Dimitrov Jan 24 '11 at 17:54
  • Apologies for being a bit dence. Would you mind showing me some code accessing those like just an alert please. – ricardo Jan 24 '11 at 17:59
  • @ricardo, you could `alert(params.param1);` or `alert(params.param2);` – Darin Dimitrov Jan 24 '11 at 18:01
  • AaaaHH Yes finally. I was trying that but had not assigned options.rules['greaterThan'] = options.params. Thanks your are a star. It is really hard to find any complete exampels. I think the naming options.rules to assign the parameters is not very intuative . 'rules; was indicative to me of having to provide the name of the function that would enforce the rule, but I now understand that it server to provide the variables required. Thanks again – ricardo Jan 24 '11 at 18:12
  • Actually could you possibly also shed some light on some of these other related question in my post ... – ricardo Jan 24 '11 at 19:22
  • 1. What is the diffirence between deriving from ValidationAttribute end implementing IClientValidatable or deriving from ModelValidator or DataAnnotatiopnModelValidator – ricardo Jan 24 '11 at 19:23
  • 2. Is 'function (value, element, params)' the standard and jQuery validation function or doe sit belong to Microsoft's unobtrusive validation – ricardo Jan 24 '11 at 19:25
  • 1
    @ricardo, concerning your first question, ModelValidator allows you to achieve the same thing as with IClientValidatable as it exposes the GetClientValidationRules method. As far as your second question is concerned, that's jquery validation. – Darin Dimitrov Jan 24 '11 at 19:33
  • So there is no compelling reason to use DataAnnotationModelValidator one over ValidationAttribute with IClientValidatable. ? – ricardo Jan 24 '11 at 19:59
  • 1
    @ricardo, ValidationAttribute acts on a single property of your model whereas a ModelValidator acts on the entire model. – Darin Dimitrov Jan 24 '11 at 20:03
  • That was what I thought to, but having read this several posts I have noted that ValidateAttribute now also as an IsValid(object value, ValidationContext context) which as I understand it give access to the whole model. So I am wondering if there is any other reason to derive from ModelValidator – ricardo Jan 24 '11 at 20:32
  • 5
    The ModelValidator approach is from MVC2 and in my opinion, should not be used going forward on an MVC3 project. The MVC3 ValidationAttribute/IClientValidatable approach is much cleaner and does not involve modifying the global.asax to register the adapter. – Paul Hiles Jan 28 '11 at 10:30
  • 1
    holy crap, this line of comments is exactly what i needed. i've been working with an mvc 2 site that i updated to mvc 3. it has a custom "RequiredIf" ValidationAttribute that uses DataAnnotationsModelValidator and all that stuff described here: http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx. I was getting so confused by the old way and these new mvc3 ways of doing it. apparently they achieve the same thing. thanks for the documentation (and heads up) on this, microsoft! *sigh* Thanks Darin, ricardo, and The Flower Guy! – kdawg Jan 28 '11 at 23:31
  • Of course, I'm still not totally set on this stuff. If someone were to post a blog entry or something describing what's new in mvc 3 and what it replaces from mvc 2 regarding emitting client-side validation from custom DataAnnotations, that'd be the bees knees! =D – kdawg Jan 28 '11 at 23:45
  • In fact DataAnnotationsModelValidator in MVC3 is still useful if the attribute is not your attribute, or if it is in another assembly which does not reference the Mvc assembly. – Softlion Apr 28 '11 at 14:04
  • @DarinDimitrov would you pls tell what is meant by this one ['greaterThan'] .. is this is the method name like this one ...'futuredate' or any other thing... – Glory Raj Jul 30 '13 at 15:57
  • How can I test such Validator? – Alexandr Nov 06 '15 at 15:25