I have been dealing with these issue for a few days, and I can't seem to be able to fix it on my own. I've already asked a question here on Stack regarding this, however I've been told this is resolved by using callback functions. I've done exactly that but the variable inside the callback still isn't changed.
I am using abide validation that is built into the Foundation framework, and I am trying to have my own custom validator for checking if an email already exists in our database. Everything works, even the console.log returns the correct results, but the variable value is not changed from false to true. Only check the code from emailVerification downwards.
Thank you for your help.
$(document).foundation({
abide : {
live_validate : false, // validate the form as you go
validate_on_blur : false,
patterns: {
positive_price: /^\+?[0-9]*\,?[1-9]+$/,
},
validators: {
positiveNumber: function(el, required, parent) {
var value = el.value.replace(/,/, '.'),
valid = (value > 0);
return valid;
},
emailVerification: function (el, required, parent) {
var email = el.value,
ajax = 1,
valid = false;
function checkServer(callback) {
$.ajax({
type: "GET",
url: "/check.do?action=userEmailAvailable",
data: "userEmail1=" + email + "&ajax=" + ajax,
success: callback
});
}
checkServer(function(data) {
if (data.result == 1) {
console.log(data.result + "does not exist");
valid = true;
} else {
console.log(data.result + "already exist");
valid = false;
}
});
return valid;
}
}
}
});
the valid that you declare in positiveNumber function is locally scoped to that function only. the next time you attempt to access it outside the function you actually create a global var called valid
As many answered, you need async validation and Abide does not support this.
I would suggest you use the excellent Parsley.js library which supports async validators through its Remote plugin
$.ajax runs asynchronously. You function returns "valid" before callback function is called. You can try running your ajax request synchronously or you need to add logic in your callback function that updates "valid" when it's called
How to create sync ajax
Add validation function to you callback
checkServer(function(data) {
if (data.result == 1) {
console.log(data.result + "does not exist");
valid = true;
} else {
console.log(data.result + "already exist");
valid = false;
}
DoCoolValidation(valid)
});
If you are sure that your server validation is fast enough you can do
function Validate(dataToSend) {
return JSON.parse($.ajax({
url: '/check.do?action=userEmailAvailable',
type: "GET",
dataType: "json",
data: dataToSend,
async: false
}).responseText);}
And use it:
valid = Validate("userEmail1=" + email + "&ajax=" + ajax);
Have a look at this post. It can help
Related
I am using jquery validation to double-check entered values. This is working find as long as my controller return true or false. Now I would like to analyze the return value first and then decide if the value is correct or incorrect.
So I am running the remote function like this:
supervisor: {
required:true,
remote: function() {
return {
url: "getADobject/" + $('#supervisor').val(),
data: '',
complete: function(data) {
console.info(data);
$(".loading").css('visibility', 'hidden');
if(data.responseJSON === false){
console.info(data.responseJSON);
//$('#supervisor').validate() = false;
//return 'false';
}
}
}
The object data returns the correct information and I also can do an if statement. But now I would like to say, yes the validation is OK or not. Can someone tell me how to add this information? I tried it already with an easy return true / 'true' / $valid = true but nothing :(
Thanks in advance
Stephan
I was wondering if my code should have return statement in my AJAX function. Here is example:
$.ajax({
type: 'POST',
url: 'Application.cfc?method=runProcess',
data: {'userID':userID},
dataType: 'json'
}).done(function(obj){
if(obj.STATUS == 200){
$('#searchMsg').addClass("success").show().text(obj.MESSAGE).delay(3500).fadeOut('slow').queue(function(){
$(this).removeClass("success").dequeue();
});
return true; //Should I keep this
}else{
$('#searchMsg').addClass("error").show().text(obj.MESSAGE).delay(3500).fadeOut('slow').queue(function(){
$(this).removeClass("error").dequeue();
});
return false; //Should I keep this
}
}).fail(function(jqXHR, textStatus, errorThrown){
alert(errorThrown);
});
As you can see I have two return statements in my function. Both will return either true or false depends on the ajax return obj. Should I keep them in my function or not? What is benefit of having them and what are the cons? If anyone can explain please let me know. Thank you!
No. Remove them. They do nothing for you. You already have the ability to know if the AJAX call succeeded or failed by which portion of your if/then in your callback function you hit.
All these statements do is send a value back to the caller that the caller then can use as it wishes and stop programmatic execution. Here, the caller is the done method call, which isn't expecting any return value and since you have the statements as the last things that will be done in the function, execution will stop anyway.
No need for this true or false if you send ajax call to check something and return server is exist they return true else false e.g Login i send ma username and password to database if exist return true than i check if ajax return true mean they user login to the system if false means user not exist but not need for true false if exist you need to open new link else give error not exist its depend on what scenario you want but not necessary to return true false its not compulsory
code example
$.ajax({
url: geturl,
type: "POST",
data:{email:email,pass:pass},
success: function (res) {
if (res == "admintrue")
{
AutoLoader("Admin Login Succeffully", "success");
var URL = $("#Afetlogin").val();
window.location.href = URL;
} else if (res == "membertrue") {
AutoLoader("Member Login Succeffully", "success");
var URL = $("#memberlogin").val();
window.location.href = URL;
}
else {
AutoLoader("Error", "error");
}
}
})
I am having an issue with some javascript logic where my function is being returned as undefined instead of the desired true/false I am looking for. I have tried everything imaginable including using callback functions and differed objects with JQuery. As soon as the AJAX call is finished, the function is undefined (right at the console.log statement). Can someone assist me?
function validateZip(countryCode, zip) {
// US validation
if (countryCode == 'US' && /^\d{5}([-\s]?\d{4})?$/.test(zip)) {
getZipJson(zip).done(function(response){
return isValid(response.results[0].address_components);
});
// international validation
} else {
return /^[A-Za-z0-9]{1,5}[-\s]?[A-Za-z0-9]{3,9}$/.test(zip);
}
}
function getZipJson(zip) {
var uri = 'http://maps.googleapis.com/maps/api/geocode/json';
// need to add google api key to data
return $.ajax({
type: 'GET',
url: uri,
dataType: 'json',
data: { address: zip }
}).success(function(response) {
console.log(response);
});
}
function isValid(address_components) {
var resultFound = false;
$.each(address_components, function (i, component) {
var types = component.types,
country = component.long_name;
$.each(types, function (j, type) {
if (type == 'country' && country == 'United States') {
resultFound = true;
return false; // break out of .each loop
}
});
if (resultFound) {
return false; // break out of .each loop
}
});
return resultFound;
}
I can get the json data just fine. The issue I am having is that after retrieving the ajax'd json and filtering through the response json, the function is coming back as undefined. I need the function to return a true/false value.
That is simply not possible, because the ajax-rewuest answers a long time after the validateZip has been called and even returned. You have to go with one of the two possibilites of asynchronous programming: continuition aka callback, or promise. (ES7 also introduces the keywords async and await, wich afaik. are just syntactic sugar for promises; like the class-keyword is just utilizes prototypal inheritance in a nicer way).
so, callback-style:
function validateZip(countryCode, zip, callback) {
if (countryCode == 'US' && /^\d{5}([-\s]?\d{4})?$/.test(zip)) {
// US validation
getZipJson(zip).success(function(response){
callback(isValid( response.results[0].address_components ));
});
} else {
// international validation
callback(/^[A-Za-z0-9]{1,5}[-\s]?[A-Za-z0-9]{3,9}$/.test(zip));
}
}
and using Promises, in this case jQuery.Deferred-objects
this code needs jQuery 1.8 or higher, since it seems that the then-method behaves differently (weird/unexpected/false) below that
https://jsfiddle.net/jge4439p/
//returns a promise
function validateZip(countryCode, zip) {
if (countryCode == 'US' && /^\d{5}([-\s]?\d{4})?$/.test(zip)) {
// US validation
return getZipJson(zip).then(function(response){
return isValid( response.results[0].address_components );
});
} else {
// international validation
return $.when(/^[A-Za-z0-9]{1,5}[-\s]?[A-Za-z0-9]{3,9}$/.test(zip));
}
}
//also returns a promise ;)
function getZipJson(zip) {
// need to add google api key to data
return $.ajax({
type: 'GET',
url: '//maps.googleapis.com/maps/api/geocode/json',
dataType: 'json',
data: { address: zip }
});
}
//an ES5-version of isValid()
function isValid(address_components) {
var isCountry = function(type){ return type === 'country' };
return address_components.some(function(component){
return (component.long_name === 'United States' || component.long_name === 'USA')
&& component.types.some(isCountry);
});
}
and the usage:
var isValidPromise = validateZip("US", "12345");
isValidPromise.then(function(v){
console.log("isValid", v);
});
or so:
validateZip("US", "12345").then(function(isValid){
if(isValid){
console.log("hooray");
}else{
console.log("ney");
}
});
at a second look, the functions getZipJson and isValid are so specific, I don't know wether I would outsorce the code. Here the combined code, again as a promise:
function validateZip(countryCode, zip) {
if (countryCode == 'US' && /^\d{5}([-\s]?\d{4})?$/.test(zip)) {
// US validation
var names = ['United States', 'USA'];
//using lambdas, because it's more compact and contains all necessary info
var isValidComponent = comp => Boolean(comp && ~names.indexOf( comp.long_name ) && ~comp.types.indexOf( 'country' ));
var isValid = result => isValidComponent( result.address_components );
return $.ajax({
type: 'GET',
url: '//maps.googleapis.com/maps/api/geocode/json',
dataType: 'json',
data: { address: zip }
}).then( response => response.results.some( isValid ) );
} else {
// international validation
return $.when(/^[A-Za-z0-9]{1,5}[-\s]?[A-Za-z0-9]{3,9}$/.test(zip));
}
}
This is because, one of the key values of Google JSON response is empty. And when your script loop thru that JSON and find one empty value it returns an undefined response (maybe one of component.types is empty or component.long_name), but this breaks all your request. I had the same problem using the Spotify API REST.
Try to console.log the full JSON object and look at the structure. If you see an empty value this may be the undefined one/s. When you parse it with an each loop, just try to replace that empty values with an empty string '' or something similar.
In this cases I think is better to code with a simple plain JS function. It gives you more flexibility.
The problem is that you are trying to get "done" response was it was sync, but it was async.
When the code bellow execute:
getZipJson(zip).done(function(response){
return isValid(response.results[0].address_components);
});
The JavaScript not wait for the ajax request is done, but it deferred the Javascript anonymous function, that was given, to be called when the ajax request is done, and the done function that is called, return a promise that will be done.
It can be see into this fiddle: http://jsbin.com/wilawiziqa/1/edit?js,console
What you can do is it:
function validateZip(countryCode, zip) {
// US validation
if (countryCode == 'US' && /^\d{5}([-\s]?\d{4})?$/.test(zip)) {
getZipJson(zip).done(function(response){
var ok = isValid(response.results[0].address_components);
continueValidation(ok);
});
// international validation
} else {
var ok = /^[A-Za-z0-9]{1,5}[-\s]?[A-Za-z0-9]{3,9}$/.test(zip);
continueValidation(ok);
}
}
function continueValidation(isZipOk){
//Continue the validation process, but now, after ajax is finished.
}
I read your reply regarding the jQuery validator where you outline a method to check a username against a value in a database.
Ive tried implementing this method but no matter what is returned from the PHP file I always get the message that the username is already taken.
Here is ths custom method...
$.validator.addMethod("uniqueUserName", function(value, element) {
$.ajax({
type: "POST",
url: "php/get_save_status.php",
data: "checkUsername="+value,
dataType:"html",
success: function(msg)
{
// if the user exists, it returns a string "true"
if(msg == "true")
return false; // already exists
return true; // username is free to use
}
})}, "Username is Already Taken");
And here is the validate code...
username: {
required: true,
uniqueUserName: true
},
Is there a specific way i am supposed to return the message from php.
Thanks
A
For anyone else who stumbles upon this, validate supports 'remote' method, which may not have existed in 2010:
https://jqueryvalidation.org/remote-method/
$("#myform").validate({
rules: {
email: {
required: true,
email: true,
remote: {
url: "check-email.php",
type: "post",
data: {
username: function() {
return $("#username").val();
}
}
}
}
}
});
You are doing an AJAX request, ergo: the validation is already finished working when your custom validator returns either true or false.
You will need to work with async. See also this post: How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?
Something like:
function myValidator() {
var isSuccess = false;
$.ajax({ url: "",
data: {},
async: false,
success:
function(msg) { isSuccess = msg === "true" ? true : false }
});
return isSuccess;
}
Warning:
As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is
deprecated; you must use the success/error/complete callback options
instead of the corresponding methods of the jqXHR object such as
jqXHR.done() or the deprecated jqXHR.success().
It took me forever to figure out how to get a jsonified string containing the value of an element in the page into the remote request- this is the result of a combination of many hours and trying many search results.
Key points:
async:false has been deprecated,
the function call right after remote: is the key for creating the data string with the element's value. Trying to access a current value from the form after data: was returning a blank value for the field with dataType set as json.
$("#EmailAddress").rules("add", {
required: true,
remote: function () { // the function allows the creation of the data string
// outside of the remote call itself, which would not
// return a current value from the form.
var emailData = "{'address':'" +
$('#SignupForm :input[id$="EmailAddress"]').val() +
"'}";
var r = {
url: "foobar.aspx/IsEmailAvailable",
type: "post",
dataType: "json",
contentType: "application/json; charset=utf-8",
cache: false,
data: emailData,
dataFilter: function(response) {
this.email_run = true; //fix for race condition with next button
return isAvailable(data); //return true or false
}
};
return r;
},
messages: {
remote: "* Email in use"
}
});
.ASPX page:
<input id="EmailAddress" required name="Email" type="email" placeholder="Email Address*" runat="server"/>
C# Code Behind:
[WebMethod]
public static object IsEmailAvailable(string address){...}
Formatting the response object:
function isAvailable(data) {
var response = JSON.parse(getJsonObject(data));
return (response.isAvailable === "True") ? true : false;
};
//transform response string to a JavaScript Object()
//http://encosia.com/never-worry-about-asp-net-ajaxs-d-again/
function getJsonObject(data) {
var msg = eval('(' + data + ')');
if (msg.hasOwnProperty('d'))
return msg.d;
else
return msg;
};
Here's my "old school" hack...
Below a utility function that allows the use of "asynchronous" validations with "jquery.validate.js" library. This function creates a delay between user keystrokes otherwise the validation function "validFunc" will be called "all time" which is not very performative in some circumstances and especially problematic for functions that perform validations on "serverside"/"backend" (ajax calls basically). In this way the "validFunc" validation function is only called when the user stops typing for a certain period of time which also allows a "realtime" validation ("onkeyup": true on jqv settings) as it occurs while the user is typing.
IMPORTANT: Validations involving the use of the "jqvAsyncValid" function should always be the last ones to avoid conflicts with the others due to the asynchrony.
{
[...]
"rules": {
"field_name": {
"required": true,
"maxlength": 12,
"minlength": 4,
// NOTE: Validation involving the use of the "jqvAsyncValid" function. By Questor
"my_custom_ajax_validation": true
},
[...]
}
ANSWER'S CODE:
// NOTE: Adds the custom validation "my_custom_ajax_validation". By Questor
$.validator.addMethod("my_custom_ajax_validation", function (value, element) {
return jqvAsyncValid(element, "my_custom_ajax_validation", myValidationFunc, this);
}, "My error message!");
// NOTE: My validation function. By Questor
function myValidationFunc(domElement) {
if (someFuncWithAjaxCall(domElement.value) == "ALL_RIGHT!") {
return true;
} else {
return false;
}
}
// NOTE: Global "json" variable that stores the "status" ("isValid") and cycle control
// ("toCtrl") of asynchronously validated elements using the "jqvAsyncValid" function.
// By Questor
var jqvAsyncVState = {};
// NOTE: A utility function that allows the use of asynchronous validations with
// "jquery.validate.js". This function creates a delay between one user keystroke and
// another otherwise the validation function "validFunc" will be called "all time"
// which is not very performative in some circumstances and especially problematic
// for functions that perform validations on the serverside/backend (ajax calls basically).
// In this way the "validFunc" validation function is only called when the user stops
// typing for a certain period of time, which also allows a "realtime" validation
// as it occurs while the user is typing. By Questor
// [Ref .: https://jqueryvalidation.org/ ]
//. domElement - DOM element informed by jqv in the "addMethod" for the anonymous
// function;
//. asyncRuleNm - Validation name added via "addMethod";
//. validFunc - Function that will do the validation. Must have the signature
// "funcName(domElement)" returning "true" for valid and "false" for not;
//. jQValidInst - Instance of the current jqv within "addMethod". It is usually
// denoted by "this";
//. toInMsecs - "Timeout" in "milliseconds". If not informed the default will be
// 1500 milliseconds. Be careful not to use a very short timeout especially in
// "ajax" calls so as not to overload the serverside/backend.
// Eg.: `return jqvAsyncValid(element, "my_custom_ajax_validation", myValidationFunc, this);`.
function jqvAsyncValid(domElement, asyncRuleNm, validFunc, jQValidInst, toInMsecs) {
if (typeof toInMsecs === "undefined" || toInMsecs === "") {
toInMsecs = 1500;
}
var domEKey = jQValidInst.currentForm.id + domElement.name;
// NOTE: The validation messages need to be "displayed" and "hidden" manually
// as they are displayed asynchronously. By Questor
function errMsgHandler() {
if (jqvAsyncVState[domEKey]["isValid"]) {
// NOTE: If the current error message displayed on the element was that
// set in the rule added via "addMethod" then it should be removed since
// the element is valid. By Questor
// [Ref.: https://stackoverflow.com/a/11652922/3223785 ,
// https://stackoverflow.com/a/11952571/3223785 ]
if (jQValidInst.errorMap[domElement.name] == $.validator.messages[asyncRuleNm]) {
var iMsgNow = {};
iMsgNow[domElement.name] = "";
jQValidInst.showErrors(iMsgNow);
}
} else {
var iMsgNow = {};
// NOTE: If the element is invalid, get the message set by "addMethod"
// for current rule in "$.validator.messages" and show it. By Questor
iMsgNow[domElement.name] = $.validator.messages[asyncRuleNm];
jQValidInst.showErrors(iMsgNow);
}
}
if (!jqvAsyncVState.hasOwnProperty(domEKey)) {
// NOTE: Set the global json variable "jqvAsyncVState" the control attributes
// for the element to be asynchronously validated if it has not already been
// set. The key "domEKey" is formed by the "id" of the "form" that contains
// the element and the element's "name". By Questor
jqvAsyncVState[domEKey] = {
"toCtrl": null,
"isValid": undefined
};
}
var useOnKeyup = true;
// NOTE: The "onblur" event is required for "first validation" that only occurs
// in a "blur" event - this is inherent to jqv - and for situations where the
// user types very fast and triggers "tab" and the event "onkeyup" can not deal
// with it. By Questor
domElement.onblur = function (e) {
jqvAsyncVState[domEKey]["isValid"] = validFunc(domElement);
errMsgHandler();
useOnKeyup = false;
}
if (useOnKeyup) {
// NOTE: The strategy with the event "onkeyup" below was created to create
// a "delay" between a "keystroke" and another one otherwise the validation
// function "validFunc" will be called "all time" which is not very performative
// in some circumstances and especially problematic for functions that perform
// serverside/backend validations (ajax calls basically). In this way the
// "validFunc" validation function is only called when the user stops typing
// for a certain period of time ("toInMsecs"). By Questor
domElement.onkeyup = function (e) {
// NOTE: Clear the "toCtrl" if it has already been set. This will
// prevent the previous task from executing if it has been less than
// "toInMsecs". By Questor
clearTimeout(jqvAsyncVState[domEKey]["toCtrl"]);
// NOTE: Make a new "toCtrl" set to go off in "toInMsecs" ms. By Questor
jqvAsyncVState[domEKey]["toCtrl"] = setTimeout(function () {
jqvAsyncVState[domEKey]["isValid"] = validFunc(domElement);
errMsgHandler();
}, toInMsecs);
};
}
return jqvAsyncVState[domEKey]["isValid"];
}
I understand that AJAX is asynchronous, and all that. But I have the following code:
function doesUsernameExist(element)
{
// Check via AJAX (POST) if username already exists in the database
var funcReturned = null;
$.post(
'_ajax_checkexist.php',
{
var: 'username',
value: element.value
},
function(data) {
funcReturned = (data.toLowerCase() == 'exists');
},
'data'
);
return funcReturned;
}
I understand why the function isn't returning a value properly, but what can I use as a workaround? This is for the Validity plugin, which requires a boolean returned by the doesUsernameExist function. I can't do the usual workaround of having the $.post function alter some HTML element. It needs to return true/false to doesUsernameExist.
What kind of workaround can I use? I'm stumped by this.
You could return a value if the call is synchronous. Here you have an example:
Solution for synchronous mode:
function doesUsernameExist(element)
{
// Check via AJAX (POST) if username already exists in the database
var funcReturned = null;
$.ajaxSetup({async: false});
$.post(
'_ajax_checkexist.php',
{
var: 'username',
value: element.value
},
function(data) {
funcReturned = (data.toLowerCase() == 'exists');
},
'data'
);
$.ajaxSetup({async: true});
return funcReturned;
}
The problem with your code, is that while the $.post is executing, the function continues it's execution (that's asynchronous), so will return null before your $.post ended.
Solution for asynchronous mode:
Using an asyncrhonous call, you cannot return a value, but can call callbacks or execute code just there. This could be an async solution:
function doesUsernameExist(element)
{
var funcReturned = null;
$.post( //This post is asynchronous
'_ajax_checkexist.php',
{
var: 'username',
value: element.value
},
function(data) {
funcReturned = (data.toLowerCase() == 'exists');
userExistanceCallback(funcReturned);
},
'data'
);
//We don't return anything
}
function userExistanceCallback(funcReturned){
if(funcReturned == true)
alert("User exists!!");
else
alert("User does not exist!!");
}
Hope this helps. Cheers
If this is for validation purposes you will find that the jquery.validate plugin and its remote rule are perfectly adapted for what you are trying to achieve.
So, instead of writing doesUsernameExist functions and worrying about asynchronousness of AJAX and how to return values you will write validation rules for your form (which in fact is the final goal of the exercise => focus on the business and leave the plumbing to plugins):
$('#id_of_some_form').validate({
rules: {
password: {
required: true
},
username: {
remote: '_ajax_checkexist.php'
}
}
});