Simian
Simian

Reputation: 864

jQuery validation, waiting for service call response before proceeding

I currently have a jQuery method that validates a UK phone number. This only validates the format and does not make sure the number is real, so I am trialing a telephone validation service that will.

The code below shows the jQuery validation method with the final IsValid(phone_number, 'GB') bit calling a function which deals with the service.

jQuery.validator.addMethod('phonesUK', function(phone_number, element) {
phone_number = phone_number.replace(/\(|\)|\s+|-/g,'');
return this.optional(element) || phone_number.length > 8 &&
    phone_number.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|8\d{7,9}|7(?:[45789]\d{8}|624\d{6})))$/)
    && IsValid(phone_number, 'GB');}, 'Please specify a valid UK phone number');

The code below handles calling the service and works fine on its own.

function IsValid(telephoneNumber, defaultCountry)

{ 
  var internationaltelephonevalidation = new data8.internationaltelephonevalidation();
  internationaltelephonevalidation.isvalid(
    telephoneNumber,
    defaultCountry,
    [
      new data8.option('UseMobileValidation', 'true'),
      new data8.option('UseLineValidation', 'true')
    ],
    showIsValidResult
  );
}

function showIsValidResult(result) {
  if (!result.Status.Success) {
    alert('Error: ' + result.Status.ErrorMessage);
  }
  else {
    if(result.Result.ValidationResult == "Invalid"){
      return false;
    }else{
      return true;
    }
  }
}

My problem is that the jQuery validator method returns false before the response from the service comes back.

I did try a set timeout in the jQuery method and set a variable before returning from the showIsValidResult() function, but the variable was always undefined.

Any ideas please?

Thanks

Upvotes: 1

Views: 506

Answers (1)

Jay
Jay

Reputation: 3515

internationaltelephonevalidation.isvalid is an async XHR call. It's being executed within the IsValid function but the function doesn't wait for the response. Instead it simply returns undefined because it is a default return statement in JavaScript.

The isValid function can be rewritten using the $.Deferred object:

var funReady;

function IsValid(telephoneNumber, defaultCountry) {
  funReady = new $.Deferred();
  var internationaltelephonevalidation = new data8.internationaltelephonevalidation();
  internationaltelephonevalidation.isvalid(
    telephoneNumber,
    defaultCountry,
    [
      new data8.option('UseMobileValidation', 'true'),
      new data8.option('UseLineValidation', 'true')
    ],
    showIsValidResult
  );

  $.when(funReady).then(function() {
    // no action needed after $.Deferred is resolved/rejected.
    // just waiting for it to happen without leaving isValid
    // function ...
  });

  return funReady.state() === "resolved" ? true : false;
}


function showIsValidResult(result) {
  if (!result.Status.Success) { // http(s) request failed
    funReady.reject();
  }

  if(result.Result.ValidationResult == "Invalid") {
    funReady.reject();
  } else {
    funReady.resolve();
  }
}

Deferred object returns a promise. By default its status is "pending", that means neither "resolved" (success) nor "rejected" (fail). While a promise is in "pending" state, $.when() function waits for its status to change to either "resolved"/"rejected".

The Deferred status is changed programmatically once the async XHR request is finished/failed.

This solution is a demo. You should consider checking if the $.Deferred is already created, instead of re-creating new instances for each isValid call. There could be other edge cases in this proposal but it should help you get to the right answer.

Upvotes: 1

Related Questions