Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery validation date range issue

I have a number of spots in my code where I have pairs of related start and end date fields (range). I need to validate that the start is before the end date. I'm using the jQuery validation plugin.

Here's my code: http://jsfiddle.net/jinglesthula/dESz2/

Markup:

<form id="myform">
  <input type="text" name="startDate" id="startDate" class="validDate" />
  <br/>
  <input type="text" name="endDate" id="endDate" class="validDate" />
  <br/>
  <input type="submit" />
</form>

js:

$(function() {
  // simple date mm/dd/yyyy format
  $.validator.addMethod('validDate', function(value, element) {
    return this.optional(element) || /^(0?[1-9]|1[012])[ /](0?[1-9]|[12][0-9]|3[01])[ /][0-9]{4}$/.test(value);
  }, 'Please provide a date in the mm/dd/yyyy format');

  $.validator.addMethod('dateBefore', function(value, element, params) {
    // if end date is valid, validate it as well
    var end = $(params);
    if (!end.data('validation.running')) {
        $(element).data('validation.running', true);
        this.element(end);
        $(element).data('validation.running', false);
    }
    return this.optional(element) || this.optional(end[0]) || new Date(value) < new Date(end.val());

  }, 'Must be before corresponding end date');

  $.validator.addMethod('dateAfter', function(value, element, params) {
    // if start date is valid, validate it as well
    var start = $(params);
    if (!start.data('validation.running')) {
        $(element).data('validation.running', true);
        this.element(start);
        $(element).data('validation.running', false);
    }
    return this.optional(element) || this.optional(start[0]) || new Date(value) > new Date($(params).val());

  }, 'Must be after corresponding start date');
  $('#myform').validate({ // initialize the plugin
    rules: {
        startDate: {dateBefore: '#endDate', required: true},
        endDate: {dateAfter: '#startDate', required: true}
    },
    submitHandler: function (form) { // for demo
        alert('valid form submitted'); // for demo
        return false; // for demo
    }
  });
});

I want to make it so that whenever one date field changes that I do a check to make sure the range is valid (e.g. start < end).

What I'm running into is that if I enter a valid range, then change the year of the end date to be the year prior to the year of the start date it will clear the error on the start date, but not clear the error on the end date field. I've debugged and stepped into the dateAfter method and seen it return true! I can even (without further modification of the field values) hit the submit button and then the error clears and the form submits.

Any idea why it's not clearing it?

edit To further clarify the steps I'm taking to reproduce:

  1. Enter 01/01/2014 in the start field (the first one)
  2. Enter 01/01/2015 in the end field
  3. Change the 5 to a 3 in the end field year
  4. Select the 3 and type 5 again
  5. start field clears it's error, but end is still complaining about range start/end
  6. click or tab out to blur the end field - still no clearing of error
  7. click end field
  8. Shift+Tab back to the start field
  9. end field now (finally) clears error message (you could also submit the form to see it clear the end field error)
  10. wai? 0 wai ??!!!!??!??!!!111!!!one!!!!11!!!1!!!
like image 931
jinglesthula Avatar asked Oct 21 '22 13:10

jinglesthula


1 Answers

Ah, figured it out (or at least solved the issue). The validation plugin was likely not designed to allow for validation of a second field within the running of a given field's method(s). Probably there's an internal reference to the field currently being validated, so you need to do the validations in serial rather than overlapping them.

I've added an update to the fiddle here where you can see that using setTimeout to ensure that the 'other field' validates after the first indeed clears up the problem: http://jsfiddle.net/jinglesthula/dESz2/3/

this.element(end);

becomes

setTimeout($.proxy(
  function () {
    this.element(end);
  }, this), 0);

I also added a setTimeout for the clearing of the 'validation.running' flag to ensure it doesn't get cleared until after the 'other' field validates.

like image 60
jinglesthula Avatar answered Oct 23 '22 02:10

jinglesthula