0

I'm lost and not exactly sure what to look up when it comes to this. I know it might have something to do with jQuery promises. So, quick to the point, i'm trying to return a "pass" variable as boolean to check whether a email is valid or not. Instead of passing false with a value = "asdf", it's still passing true. I know it's because of the asynchronous request, I'm just not exactly sure how to defer that variable. Code is below:

console.log( this.newValidate($('#forgottenPwdForm'))); // Returns true

newValidate: function(form){
    var $form  = form,
        errmsg = function(msg){
            return '<span class="error" style="text-align:right; margin: 2px 15px; color:red;">' + msg + '</span><br/>';
        };

    // Check email / username
    // Needs to run 2 validations. Is the email valid? And is it duplicate if valid
    if($form.find('.email').length)
        $form.find('.email').each(function(){
            var email = escape($(this).val().trim()),
                valid = true,
                duplicate = false,
                pass = true;

            // Check if email is valid
            $.when(
                $.get('/service/account/ajaxdata?method=validemail&emailaddr='+email, function(res){
                    console.log(res);
                    valid   = res;
                }),

                $.get('/subscribe/check_email?email=' + email, function(data) {
                    if(data){
                        duplicate   = false; }
                })

            ).then(function(){

                if(valid == 0){
                    var error = errmsg("Email is not valid.");
                    pass = false;
                    console.log(pass);
                }
                else {
                    // Now that the email is valid, we need to check if it's duplicate
                    if(duplicate == false) {
                       $('.email').addClass('emailError');
                       pass = false;
                    }
                    else {
                       if($('.email').hasClass('emailError')) {
                          $('.email').removeClass('emailError');
                          $('.email').removeClass('error');
                       }

                       pass = true;
                    }
                }

            });

            if(pass == false) return pass;
        });

Code returns true when it should be returning false. Again, I know it's something to do with the $.get request and the variable being out of scope, I'm just not sure how to defer it.

jamadri
  • 926
  • 3
  • 17
  • 32
  • It was looking good until this line `if(pass == false) return pass;` which is outside the promise, so gets executed first. You simply can't do "if (isvalid()==true)` type tests when `isvalid()` makes an ajax call (but I suspect you already knew that). See here for more info: http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call/14220323 – freedomn-m Mar 14 '17 at 15:08
  • I understand that, but the newValidate method, which the entire method, actually has more checks in it such as checking for the '.required' / '.passwd' classes as well and returns false based on whether those pass or not. And a false return keeps the form from getting posted (which isn't shown). Checking that link currently. – jamadri Mar 14 '17 at 15:12

2 Answers2

2

Inside newValidate(), you are using promises, therefore return a promise. Please don't be tempted to pass in a callback "because then you lose exception bubbling (the point of promises) and make your code super verbose" (@Esailija).

This is a fairly challenging introduction to promises, therefore lots of comments :

newValidate: function($form) {
    var $emailElements = $form.find('.email');
    var promises = $emailElements.map(function(index, el) { // Use `.map()` to produce an array of promises.
        var email = escape($(el).val().trim());
        return $.when(
            $.get('/service/account/ajaxdata?method=validemail&emailaddr=' + email), // no direct callback here ...
            $.get('/subscribe/check_email?email=' + email) // ... or here.
        ).then(function(valid, unique) { // Simple! The two $.get() responses appear hear as arguments.
            // The question's `data` appears to be a `unique` indicator (ie !duplicate), but the sense may be reversed?
            var pass = valid && unique; // A composite pass/fail boolean for this email element.
            if(!pass) {
                $(el).addClass('emailError');
            } else {
                $(el).removeClass('emailError');
            }
            // Note: It would be better to give the user separate indications of invalid|duplicate, so (s)he has a better clue as to what's wrong.
            return pass; // whatever is returned here will be the value delivered by the promise inserted into the `promises` array
        });
    });
    // Now use `$.when()` again to aggregate the `promises` array. 
    return $.when.apply(null, promises).then(function() {
        // Now use `Array.prototype.reduce` to scan the arguments list (booleans) and give a composite pass/fail boolean.
        var pass = Array.prototype.reduce.call(arguments, function(prev, current) {
            return prev && current;
        }, true);
        if(!pass) {
            return $.Deferred().reject(new Error(errmsg("At least one email is not valid."))).promise(); // jQuery's cumbersome way to `throw` an error from a promise chain.
        }
    });
}

Call as follows :

this.newValidate($("#myForm")).then(function() {
    // pass
}, function(error) {
    // Something went wrong.
    // Expected error or unexpected error will end up here.
    consoe.log(error);
    $("#whatever").append('<div class="error">' + error.message + '</div>'); // if required
});
Community
  • 1
  • 1
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
  • This is actually a much better explanation. First off, I want to thank you for going through the time to write this explanation up. Second, this definitely represents more so what I was looking for, and simply just by reading the comments through your code I'm seeing how promises work better. Again, thank you! – jamadri Mar 15 '17 at 14:14
0

Instead of setting a variable you need to execute a callback method with the result.

The code is running Asynchronously so execution continues right to the end without executing any of your validation code, that is why you always get true.

so instead of saying pass = true|false you need to do something like: MyCallBackFunction(true|false)

Carlos Roque
  • 448
  • 5
  • 11