views:

199

answers:

2

In the jQuery Validation plugin there are a couple of methods that do the same thing, but for different locales, for example dateISO and dateDE, that both validate date formatting. How do I combine these so that the input element accepts either?

Let's say I have an <input type="text" name="dateInput" id="dateInput" /> in my form, and I want to allow users to enter only dates in this field. However, I want to allow for several different date formats - for example, the user should be able to enter either an ISO date or a date formatted accoding to German date rules.

If I do

rules: { dateInput: { required: true, dateISO: true, dateDE: true } }

the form will never be valid, as both date formats will be required and that requirement can never be fulfilled. Is there a way to combine these as an "or" instead of as an "and", without having to write my own validation method?

And if I do have to write my own, how do I make it as generic as possible?

A: 
dateISO: function(value, element) {
     return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
},
dateDE: function(value, element) {
     return this.optional(element) || /^\d\d?\.\d\d?\.\d\d\d?\d?$/.test(value);
}

source

lets combine it... :)

jQuery.validator.addMethod("dateISOorDE", function(value, element) { 
    return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value) || /^\d\d?\.\d\d?\.\d\d\d?\d?$/.test(value); 
}, "Please specify the correct date");
Reigel
Yeah, I figured I could do this. But that means that each time I want to combine two validation methods, I'd have to do this work all over again. And the same thing goes for adding support for a third one in this method. What if I decide I want to support Swedish dates as well? What if I need support for multicultural numbers(not all cultures use "." for decimal markers - Sweden, for one, uses ","...)? The approach you suggest is a valid last resort, but I'd much rather have something generic and reusable.
Tomas Lycken
Now that I think about it, just being able to reference other functions in my new function is good enough as an intermediate solution. Instead of (like you) copy-pasting code, is there any way to actually *call* the other validation methods in my new method?
Tomas Lycken
+1  A: 

While you can combine the regexes like Reigel has, you can also just call those methods directly (in case they change!), like this:

$.validator.addMethod("dateISODE", function(value, element) { 
    return $.validator.methods.dateISO.apply(this, arguments) 
        || $.validator.methods.date.apply(this, arguments); 
}, "Please enter a valid ISO or German date");

Now I have date instead of dateDE here, because in newer versions of the plugin dateDE was removed. It's now in a localization file that just overrides the date method. If you're using an older version that's fine, just stick with dateDE.

You can try a demo here


Update for comments: A more generic form would look like this:

$.validator.addMethod("oneOf", function(value, element, params) {
  for(p in params) {
    if(params.hasOwnProperty(p) && 
       $.validator.methods[p].apply(this, [value, element, params[p]]))
      return true;
  }
  return false;
}, "Please enter a valid date");

The rules would look like this:

$("form").validate({
  rules: {
    dateFieldName: { oneOf: { dateISO:true, date:true } }
  }
});

You can try a demo here, this takes any number of validator functions and runs them, at least one must return true for it to succeed.

Nick Craver
Ideally, I'd like to send in an options object of the same syntax as the one I use to apply rules, and then combine all of them in a way similar to what you describe. Is this possible? (I have tried, but I keep running into problems with me not knowing what my function context is...)
Tomas Lycken
@Tomas - `rules` doesn't iterate like that, so I'm not sure what you can accomplish that route, each rule **inside** this element's rules object is what executes, so this is one of those, being `dateISODE: true` in this case. You're wanting to go up a level, saying `dateInput:....something`, I don't even know what the syntax would look like. In short, no, I don't think you're going to get out of using custom methods for this, at least not without modifying the validation plugin's core code...that's a path to stay away from :)
Nick Craver
Maybe I've misunderstood how `rules` work, or expressed myself vaguely. What I'm after is being able to declare the rules with syntax like `rules:{ myinput: { oneOf: {dateISO:true,dateDE:true} } }`. That *would* be possible if I could iterate over the arguments to `oneOf`, and call those functions with their correct arguments - are you saying this is *per se* impossible?
Tomas Lycken
@Tomas - That description was much clearer...I updated the answer to show how to do exactly that :)
Nick Craver
Brilliant - that's **exactly** what I was after! Kudos! =)
Tomas Lycken