0

I have an array of validation checks and one happens to be using ajax to check for an address. I needed the entire array to return true and fire sequentially before submitting a form. I tried using promises but to no avail.

Here's the issue. If I either enter an incorrect address or leave the input(s) blank then it doesn't submit the form (good). But when I actually enter a valid address the form submits despite the fact that my other validations in the validations array have false values. What am I doing wrong?

var validations = [validateInputPresence, validatePassword, validateAddress];

$continueButton.on('click', function() {    
  toggleSpinner();

  subscribe().then(function() {
    submitForm(); // This is firing even when some values are false in the array
  }, function() {
    toggleSpinner();
  });
});

function subscribe() { 
  var promises = validations.map(function(validation) {
    return validation();
  });

  return Promise.all(promises);
}

function validatePassword() {
  var password = $password.val();
  var format = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])[^+&\n]{8,}$/;

  return validateInput(format.test(password));
}

function validateAddress() {
  return new Promise(function (resolve, reject) {
    $.ajax({
      type: 'POST',
      url: '/address/validate',
      data: $form.serialize(),
      dataType: 'json',
      success: function(response) {
        var hasValidAddres = response.Data === 200;

        validateInput(hasValidAddres);

        hasValidAddres ? resolve() : reject();
      }, 
      error: function() {
        toggleSpinner();
      }
    });
  });
}

function validateInput(validation) {
  if (validation) {
    return true;
  } else {
    return false;
  }
}
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
  • Could you show the content of functions: validatePassword and validateAddress? – Jakub Rożek May 09 '16 at 18:40
  • I updated the code with the `validatePassword()` function and the `validateInput()` function that shows how the true/false values are returned. – Carl Edwards May 09 '16 at 18:44
  • Validate password doesn't seem to return a promise – yBrodsky May 09 '16 at 18:46
  • `validation()` is the current function that's looped through inside `subscribe()` – Carl Edwards May 09 '16 at 18:47
  • Yea, my bad. I edited – yBrodsky May 09 '16 at 18:48
  • Do **all** of the validations need to return a promise in order for this to work even if they don't use ajax? – Carl Edwards May 09 '16 at 18:49
  • @CarlEdwards When you serialize the data into json, isn't it just a string until it's parsed? So the array is not an array nor are the booleans not 0/1? So if it's a string it's always going to be truthy? – zer00ne May 09 '16 at 18:51
  • Not sure I follow you. Are you referring to the serialized data in the `validateAddress()` function? – Carl Edwards May 09 '16 at 18:54
  • Yes, if this is asynchronous, then isn't it possible no matter what the input, it's just processing a string? – zer00ne May 09 '16 at 18:56
  • Your `validateInput` function doesn't really make sense. It's equivalent to the `Boolean` function. – Bergi May 09 '16 at 21:14
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! See [here](http://stackoverflow.com/a/31327725/1048572) how to work with jQuery and native promises. – Bergi May 09 '16 at 21:16

2 Answers2

1

The main problem is that you don't return the Promise in the validateInput() function. You can't reject a promise returned by Promise.all via returning a false in one of its function. Read more about Promise.all, the quote below is taken from MDN

If something passed in the iterable array is not a promise, it's converted to one by Promise.resolve.

So actually anything, but Promise will be treated as being resolved.

What you should do is write the validateInput function to return a promise.

function validateInput(validation) {
  return new Promise(function(resolve, reject) {
    if (validation) { 
      resolve();
    } else {
      reject();
    }  
  });
}
Jakub Rożek
  • 2,110
  • 11
  • 12
  • It now seems that the form submits regardless. – Carl Edwards May 09 '16 at 19:05
  • @CarlEdwards my bad, your implementation of subscribe should be correct, so use yours instead of mine. – Jakub Rożek May 09 '16 at 19:11
  • With the current implementation of returning a promise inside the `validateInput()` function I get `Uncaught (in promise) undefined` for every validation that's being looped through now. – Carl Edwards May 09 '16 at 19:22
  • That's fine when the validation is false, are you sure you pass true to validateInput()? You will get that error when the input is invalid. – Jakub Rożek May 09 '16 at 19:26
  • Great to know. New to promises so the first thing I think when I see an error in the console was that it was "my fault". Thanks so much for your help. +1 for helping me get out of a tight jam. – Carl Edwards May 09 '16 at 19:38
  • That's actually a good feature - it's helpful in debugging. You are likely to get such results in the console when the promise fails, so it doesn't have to mean you have a bug in code. It just says the promise was rejected and that's it - the code doesn't have to break up. It also a good think to catch promises (just add .catch(func) to your chain) to handle the rejections and provide some other action if necessary. – Jakub Rożek May 09 '16 at 19:47
0

What am I doing wrong?

You've never tested whether the values in the array were true or false. You did most of the promise stuff correctly, and subscribe() returns a promise for an array of boolean values. If you want to test whether all of them are true, you will need to do that explicitly:

subscribe.then(function(results) {
    if (results.every(Boolean)) // all are true
        submitForm();
    else // some are false
        …;
}, function(err) {
    // some validations threw an exception
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375