6

OK, I give up. After trying endless permutations of inline functions, callbacks, return parameters, I still cannot get this to behave how I want. Any help would be appreciated.

Current status: everything works except the 'checkEmail' error message. The javascript popups convey the proper messages to the user, but the field will not show any error message, whether true or false is returned by the handlers.

The desired outcome is that if the JSON payload from the AJAX call is anything other than a 200, to pop a Javascript alert and highlight the field as invalid with the appropriate error message. Currently, it pops the alerts, but doesn't mark the field as invalid.

(Sorry, this is jQuery 1.7 + jquery.validation plugin)

JSON responses:

Our JSON response payloads look like this:

{"message": "Email is not in use.", "code": 200, "email": "test-39485787@mailinator.com"}

or

{"message": "Duplicate email address detected", "code": 401}

The code

<html>

<head>
<script>

// You'd think this should be false, but no
// Also it apparently needs to be a quoted 
// string: http://stackoverflow.com/a/11496502/814669
var checkEmailStatus = "true";

// Success Handling
checkEmailSuccess = function(data, textStatus, jqXHR) {
    checkEmailStatus = "true";
    switch (data.code) {
        case 200:
            checkEmailStatus = "false"; // Nothing set here matters
            return false; // Return valued from here don't matter
        case 401:
            alert("Sorry, our system has detected that an account with this email address already exists.");
            break;
        case undefined:
            alert("An undefined error occurred.");
            break;
        default:
            alert("An undefined error occurred.");
            break;
    }
    return checkEmailStatus;
};


// Error Handling
checkEmailError = function(jqXHR, textStatus, errorThrown) {
    if (jqXHR.status == 404) {
        alert("Could not find the API Server (404). We apologize for the inconvenience.");
    } else {
        alert("Terrible things have happened" + textStatus);
    }
    return false;
};


// The Validator
jQuery.validator.addMethod("checkEmail", function(value, element) {

    $.ajax({
        type: "POST",
        cache: "false",
        async: "false",
        url: "/json/checkEmail",
        dataType: "json",
        data: {
            email: function() {
                return $("email").val();
            }
        },
        success: checkEmailSuccess,
        error: checkEmailError
    });

    // Seems checkEmailStatus value never gets changed:
    return checkEmailStatus;

}, "Email Address Problem");


// The Binding
$(document).ready(function(){
    $('#MyForm').validate({
        onfocusout: false,
        onkeyup: false,
        rules: {
            "email": {
                required: true,
                minlength: 2,
                maxlength: 42,
                email: true,
                checkEmail: true
            }
        }
    });
});

</script>
</head>

<body>

    <!-- the Form -->
    <form id="MyForm">
        <input type="text" name="email"/>
        <input type="submit" name="submit"/>
    </form>

</body>
</html>
Spanky
  • 5,608
  • 10
  • 39
  • 45
  • I would change the title to a question e.g. "How do I create a custom validator in jQuery?" Or something like that. Then, "This is what I tried and where I went wrong". All in a narrative, before you get into the details. – aclark Jun 07 '13 at 21:28
  • Good idea. The custom validator isn't the problem, and actually works perfectly, it's the result of the post processing that's the problem. What good is making an AJAX call if you can't do anything with the response or all you get is "true or false". – Spanky Jun 07 '13 at 21:51

3 Answers3

16

The correct answer here is to indeed use the "remote" option, but do all of your response processing using "dataFilter":

checkEmailSuccess = function(response) {
    switch (jQuery.parseJSON(response).code) {
        case 200:
            return "true"; // <-- the quotes are important!
        case 401:
            alert("Sorry, our system has detected that an account with this email address already exists.");
            break;
        case undefined:
            alert("An undefined error occurred.");
            break;
        default:
            alert("An undefined error occurred");
            break;
    }
    return false;
};

"email": {
    required: true,
    minlength: 2,
    maxlength: 42,
    email: true,
    remote: {
        url: "/json/checkEmail",
        type: "POST",
        cache: false,
        dataType: "json",
        data: {
            email: function() { return $("#email").val(); }
        },
        dataFilter: function(response) {
            return checkEmailSuccess(response);
        }
    }
},
Spanky
  • 5,608
  • 10
  • 39
  • 45
3

The addMethod expects a true/false response inmediately and you are doing an AJAX call which is an async operation so the variable checkEmailStatus will return its initial value. Please look into "remote" validation, this is what you need:

http://jqueryvalidation.org/remote-method/

Update: I just noticed the async = false. Try to avoid that option. It reduces the user experience.

Try this:

$('#MyForm').validate({
    onfocusout: false,
    onkeyup: false,
    rules: {
        "email": {
            required: true,
            minlength: 2,
            maxlength: 42,
            email: true,
            //checkEmail: true
            remote:{
                url: "/json/checkEmail", //make sure to return true or false with a 200 status code
                type: "post",
                data: {
                    email: function() {
                        return $("email").val();
                    }
                }
            }
        }
    }
});
epignosisx
  • 6,152
  • 2
  • 30
  • 31
  • 1
    1. the AJAX call is marked async = false; 2. I tried using remote but it wasn't robust enough. I need to do all of this handling. All of my efforts to use remote failed. I've read all the SO and j.validation documentation on that already to no avail. – Spanky Jun 07 '13 at 21:29
  • If you'd be willing to provide an example that accomplished the same as above, I'd be happy to give it a try. :-) – Spanky Jun 07 '13 at 21:32
  • True! I missed the async = false. I updated the answer with an example. – epignosisx Jun 07 '13 at 21:38
  • I'm not clear how this solves the issue. How does this code parse the JSON response and handle different response codes? I can get 200, 401, 403 (and a few others) back in the payload, as shown by the switch/case block. I tried putting all of that code into the remote method and it wasn't respected there either. I can try again... – Spanky Jun 07 '13 at 21:40
0

If you want to use actual http response codes, you can use the error function of the $.ajax call, it will work inside the validation plugin.

remote: {
    url: "/json/checkEmail",
    type: "POST",
    cache: false,
    dataType: "json",
    data: {
        email: function() { return $("#email").val(); }
    },
    error: function(jqXHR, textStatus, errorThrown) {
        alert(jqXHR.status);
        alert(textStatus);
        alert(errorThrown);
    }
bblue
  • 545
  • 5
  • 24