I'm not massively experienced with JavaScript and I'm having trouble with variable scope and jquery. I have the following structure:
function pass_variables()
{
username = "efcjoe"
response = post_variables(username)
alert(response)
}
function post_variables(username)
{
$.post(
'/path/to/url/',
{
'username': username,
},
function(data)
{
valid = (data != 0) ? true : false
// OPTION 1: If I put return here...
return valid; // ... the alert box in pass_variables says "undefined"
},
"text"
);
// OPTION 2: If I put return here...
return valid; // ... The alert box does not pop up, and Safari debug gives
// me the error: "Can't find variable: valid"
}
Am I missing something there? I think valid should be a global variable, and therefore option 2 should work fine. I'm really not sure about option 1.
Can anyone give me any advice on the best way to get this working?
Thanks a lot.
Ajax calls are asynchronous which means they get called but do wait around for execution to complete. Basically your alert is firing before the ajax request has completed and run the callback function to change your variable.
The best thing you can do is actually pass a function to run when the ajax request has completed. This also negates the need for global variables which are frowned upon since other plugins, script can alter their state and leave your script open to errors, flaws etc
E.g
function foobar(){
//call function to do post request and also pass a function to run
//when post has returned
runPostRequest( callbackFn );
}
function runPostRequest(callback){
$.post( '/foo', callback );
}
function callbackFn( data ){
console.log('post request complete');
}
In your option 1 you are returning from the callback function, and its return value is never used because this function is only called when the Ajax request ends.
In the option 2, you are returning from your main function, but that return happens before the callback function assign any value to your valid variable.
I would refactor your code in this way, without using global variables:
function post_variables(username){
$.post('/path/to/url/',{
'username': username,
},
function(data){
var valid = data != 0;
// OPTION 3: Work in the callback function
alert(username);
alert(valid);
// OPTION 4: Pass the values and work on another function
otherFunction(username, valid);
},"text");
}
function otherFunction(username, isValid){
//...
}
Yeah, your problem is that you're not grasping some order of operations issues here. The function you're passing to $.post is a callback; it runs later, considerably after post_variables() finishes. post_variables() itself does not wait for the $.post to finish, so valid doesn't exist when you're trying to use it.
Remember that AJAX is asynchronous. The return valid; gets executed immediately after the $.post() is set up, but before the post has completed (and therefore, before valid is defined). What you probably want to do is this:
function post_variables(username)
{
var username = "efcjoe";
$.post(
'/path/to/url/',
{
'username': username,
},
function(data)
{
var valid = (data != 0) ? true : false
alert(valid);
},
"text"
);
}
And note that this no longer needs global variables, but function-scope variables that are declared using var.
You could solve the problem quite easily by assigning it a function instead of an inline one, and the event function does the alert:
function pass_variables()
{
username = "efcjoe"
response = post_variables(username);
}
function post_variables(username)
{
$.post(
'/path/to/url/',
{
'username': username,
},
receivedData,
"text"
);
}
function receivedData(data)
{
valid = (data != 0) ? true : false;
alert(valid)
}
Related
I am trying to pass a variable from a form field to a function which in returns checks if the value exists from the database and returns either exists or not_exist, but this it returns undefined.
registerButtonAction.addEventListener('click', function() {
disableRegisterButton();
var getRegisterUsername = document.getElementById('registerUsername').value;
var confirmUser = validatedUser(getRegisterUsername);
alert(confirmUser); //alerts undefined instead of exist, or not_exist
if (confirmUser === 'not_exist') {
alert('Okay');
}
});
function checkRegisterUsername(getRegisterUsername){
$.ajax({
type:"post",
url: "accounts/users/checkUsername/"+getRegisterUsername,
data:{getRegisterUsername:getRegisterUsername},
success: function(data){
if(data==='exists'){
var a='exists';
return a;
}else if(data==='not_exist'){
var a='not_exist';
return a;
}else if(data==='navigation_error'){
enableRegisterButton();
document.getElementById('resistrationStatus').innerHTML="<small style='font-size: 15px; color: #ac2925'><i class='fa fa-info-circle'></i> FATAL ERROR OCCURRED, RELOAD PAGE</small>";
}else{
enableRegisterButton();
document.getElementById('resistrationStatus').innerHTML="<small style='font-size: 15px; color: #ac2925'><i class='fa fa-info-circle'></i> FATAL ERROR OCCURRED, RELOAD PAGE</small>";
}
},
error: function() {
alert("Error Occured, Check your connection and try again.");
}
});
}
I guess your validateUser(..) function will call your ajax. In your case i think your ajax call isn't finish yet when alert is fired.
Try to move this part at the end of ajax's success. Or you can either add async: false as paremeter of ajax. With async to false it will wait for the end of the call before move on.
I suppose that validatedUser and checkRegisterUsername are the same function, and you forgot to change the names.
AJAX calls are tipically asynchronous. This means that you can't wait for a return value: success function will be executed when the AJAX call will succeed, but in a separate context.
Even if you make the AJAX call synchronous, return in a success/error callback is meaningless. Also, the return value you are trying to catch is the one of checkRegisterUsername, that does not have a return status at all, that's why is undefined.
To achieve what you want, try to put the alert part inside the success handler, i.e. instead of return a write alert(a) and see what happens.
I realize SO is loaded with material on this subject, I've gone through it, but just can't seem to get this to work.
My function takes a JSON response, parses it and returns a false on success and true on error (This is backwards on purpose).
console.log shows I'm getting the correct response, but my function won't return (submitme remains false although it is changed to true inside the callback). Thus, the function always returns false.
I'm guessing my callback operation is faulty but I'm not sure what I'm doing wrong.
I might be off here, but I think the callback is OK, it is just being executed after the script is executed (hence asynchronous).
But my question remains, how in this case can I have the form submit true if the boolean value is only being changed via ajax?
Here's my snippet:
$(".form").submit(function(){
error = $("#error");
var submitme = false;
function ajaxCall(url, type, data, callback) {
$.ajax({
url: url,
type: type,
data: data,
success: function(response){
try {
obj = JSON.parse(response);
text = obj.Error;
error.html('');
error.append(text);
//if the json was parsed, return false
submitform = false;
}
catch (error) {
//if an error was thrown, return true
submitform = true;
}
callback(submitform);
},
});
}
function ajaxCallSuccess(submitform) {
console.log(submitform); //This shows correct output!
if (submitform == 'true'){ //Here I want to manipulate the global submitme variable so I can submit the form on "true"
submitme = true; //<-- this (I think) changes the global variable asynchronousy (so after the function is read), hence the outer function always returns false
}
};
ajaxCall(url, type, data, ajaxCallSuccess);
if (submitme == true) {
return true;
}
else {
return false;
}
});
EDIT:
I edited the post to perhaps clarify the question a little bit
You need to always return false and then manually submit the form in case the AJAX call succeeds:
$('.form').submit(function() {
function ajaxCall(url, type, data, callback) {
$.ajax({
url: url,
type: type,
data: data,
dataType: 'json',
success: function(response) {
callback(true);
},
error: function() {
callback(false);
}
});
}
var form = $(this);
function ajaxCallSuccess(submitform) {
if (submitform) {
// submit the form here:
form[0].submit();
}
};
ajaxCall(url, type, data, ajaxCallSuccess);
return false;
});
I realize SO is loaded with material on this subject, I've gone through it, but just can't seem to get this to work.
Why? Have you not understood what was explained there …?
But my question remains, how in this case can I have the form submit true if the boolean value is only being changed via ajax?
You can not return a value out of an asynchronous handler, and expect that value to be available in the code that follows it, because of the asynchronous execution. The following code will not “wait” for the asynchronous action to be finished, that’s the whole point of asynchronism.
So cancel the submitting of the form right away – and then submit the form from within your callback function if applicable.
I'm having trouble getting a custom validation rule setup with knockout.js for checking if a username already exists. From my understanding, if the return is true then there are no errors, otherwise an error is set.
Here's an example of custom validation
//val is the username in question and searchType is the type of search(username or email)
function checkValue(val, searchType){
if(searchType == 'userName'){
$.post("/users/check_if_exists",{ 'type':'username', 'value': val },function(data) {
var info = JSON.parse(data);
if(info.username_availability == "available"){
return searchType;
//I know this is working because I've alerted the searchtype here and it displays properly
}
else{
return "unavailable";
}
});
}
}
ko.validation.rules['checkIfExists'] = {
validator: function (val, searchType) {
return searchType == checkValue(val, searchType); //if the username is availble, the searchType is returned back so it would return searchType == searchType which should be true meaning there are no errors
},
message: 'This username is taken, please select another.'
};
ko.validation.registerExtenders();
I've checked the network tab and the POST is returning the correct value. If the value is available, I return the searchType. That way, it compares searchType == searchType which should be true. However, that's not the case.
Is there any other way to accomplish what I'm trying to do?
update
Here's what I have as of now
function checkValue(val, searchType, callback) {
var callback = function(data) {
return true;
}
$.post("/users/check_if_exists", { 'type':'username', 'value': val }, function(data) {
info = JSON.parse(data);
if(info.username_availability == "available"){
callback(true);
} else {
callback(false);
}
});
}
ko.validation.rules['checkIfExists'] = {
async: true,
validator: function (val, searchType) {
alert(checkValue(val, searchType));//returns undefined
return checkValue(val, searchType);
},
message: 'This username is taken, please select another.'
};
ko.validation.registerExtenders();
ko.validation calls your validation function.
There are two types of validation functions with regards to the way they pass the success/failure state back to ko.validation: the straight-forward return true/false way, or the "async" way.
The async way exists only because $.ajax exists and it's basically just this: instead of returning a value (which is impossible, as you are using an $.ajax call) you have to somehow notify ko.validation after the ajax response gets back to the browser, right?
So the smart people who wrote this library call your validation function with an extra parameter, a function (callback), that you have to call when the response is available.
function checkValue(val, searchType, callback){
if(searchType == 'userName'){
$.post("/users/check_if_exists",{ 'type':'username', 'value': val },function(data) {
var info = JSON.parse(data);
if(info.username_availability == "available"){
callback(true);
//I know this is working because I've alerted the searchtype here and it displays properly
}
else{
callback(false);
}
});
}
}
ko.validation.rules['checkIfExists'] = {
async: true,
validator: checkValue,
message: 'This username is taken, please select another.'
};
ko.validation.registerExtenders();
Let me turn your code into something more readable:
function checkValue(val) {
var callback = function(data) {
return 'bar';
}
$.post('url', 'data', callback);
}
var x = checkValue('foo');
You are expecting x to be set to 'bar' when that is not the case.
I replaced your anonymous function with "callback" so you better understand that returning something from it does not mean "checkValue" will return anything.
After that your next problem is the fact that AJAX calls are asynchronous.
A validator function accepts three parameters: the value, extra parameters and a callback function. The callback function parameter is designed to aid you in exactly this situation.
Inside the success callback for the $.post you just have to call that callback function for the validator function passing true/false as a parameter.
function checkValue(val, params, callback) {
$.post('url', 'data', function(data) {
if(data === 'bar') {
callback(true);
} else {
callback(false);
}
});
}
checkValue as you have written it always returns undefined. Invoking "return" from inside of a callback function only sets the return value for that callback function (not the "outer function"). If the validator function expects a return value right away (as knockout appears to do) you won't be able get that data asynchronously.
I have a javascript function that posts data to a validation script and grabs a value from there. The callback function on the post request returns a boolean value, and I'm trying to get the entire function to return that boolean value. Right now, the callback function returns the correct value, but the function itself doesn't return anything. Here's the code:
function validate(request_type, request_text) {
$.post("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text
}, function(data) {
return (data == "valid");
});
}
I realise that this is sort of a "synchronous" call, and that's not what AJAX is about, but I already have numerous functions in validate.php (database calls, etc.) that I can't implement in Javascript, and I saw threads like this one that talk about using some form of handler.
How would I write a simple handler that will make either the variable data or the result of the boolean comparison data == "valid" available when I use it in an if/else statement (which is where this function is supposed to be used)?
EDIT: For example, one of the if statements that will be using the boolean result:
if (!validate('password',pass_new)) {
$('#pass_new').addClass('error');
$('#pass_confirm_new').addClass('error');
$(error_string.format('Please enter a valid password.')).insertAfter('#pass_confirm_new');
$('#pass_text_short').hide();
$('#pass_text_long').show();
EDIT: The function called with the onsubmit event in my HTML form:
function valid_pass_sett() {
//code to remove errors left over from previous submissions - snipped
pass_old = $('input[name=pass_old]').val();
pass_new = $('input[name=pass_new]').val();
pass_confirm_new = $('input[name=pass_confirm_new]').val();
//some if statements that don't involve AJAX requests - snipped
if (!validate('password',pass_new)) {
$('#pass_new').addClass('error');
$('#pass_confirm_new').addClass('error');
$(error_string.format('Please enter a valid password.')).insertAfter('#pass_confirm_new');
$('#pass_text_short').hide();
$('#pass_text_long').show();
return false;
}
return true;
}
I haven't edited this code to include the updated code that's been posted, but my question is how I return false from it to stop form submission?
function validate(request_type, request_text, callback) {
$.post("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text
}, function(data) {
callback(data == "valid");
});
}
And usage would be:
validate(request_type, request_text, function (isValid) {
if(isValid) {
// do something
} else {
// do something if invalid
}
});
Unless you make a synchronous AJAX call (which you probably don't want to do), you simply can't.
If this function is used in several places in your code, your best bet may be to allow it to receive a function.
That way instead of relying on the result being returned from your function to be used in some code, you're actually passing your code directly in, so it is ensured to be able to use the response.
var my_form = $('#my_form');
my_form.submit( valid_pass_sett );
function valid_pass_sett() {
//code to remove errors left over from previous submissions - snipped
pass_old = $('input[name=pass_old]').val();
pass_new = $('input[name=pass_new]').val();
pass_confirm_new = $('input[name=pass_confirm_new]').val();
validate('password', pass_new, pswd_validation_callback); // async validation
return false; // cancel form submission
}
function validate(request_type, request_text, callback ) {
$.post("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text
}, callback );
}
function pswd_validation_callback( data ) {
if ( data === 'valid' ) {
// if valid, call the native .submit() instead of the jQuery one
my_form[ 0 ].submit();
} else {
// Otherwise do your thing for invalid passwords.
// The form has already been canceled, so no concerns there.
$('#pass_new').addClass('error');
$('#pass_confirm_new').addClass('error');
$(error_string.format('Please enter a valid password.')).insertAfter('#pass_confirm_new');
$('#pass_text_short').hide();
$('#pass_text_long').show();
}
}
EDIT: Changed to use code posted in question.
EDIT: Updating to work with additional code posted. Narrowing answer down to the named function for clarity.
function validate(request_type, request_text) {
$.post("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text
}, function(data) {
return (data == "valid");
}); }
You can't really return from 'validate' the result of the AJAX call. You could try declare a variable before the $.post call, let's call it 'x', and inside the response function assign the value to that variable (x=data=="valid"), and outside the $.post block, but inside the 'validate' function, return that value.
function validate(request_type, request_text) {
var x;
$.post("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text
}, function(data) {
x = data == "valid";
});
return x; }
The real problem is that the function 'validate' will continue even if the post call haven't return any value, so it will always be 'false'.
The best thing you can do is call another function INSIDE the response function, so you can assure that the server call is over before getting to the next part.
Edit:
It's been a long time since I posted this answer. The world has changed and so AJAX calls.
Now we have promises ;)
You still cannot return a direct value from a function, but you can return a Promise object, which can be chanined to another Promise, and the second promise will get the data you returned from the first one.
function validate(request_type, request_text) {
var promise = $.ajax("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text
});
function getStuff(data) {
//Do something and return the data to the next promise
return data;
}
promise.then(getStuff).then(function(data){
// Do something else with data
});
}
If I understand this question correctly you can achieve what you want by simply storing the returned value into a HTML element and then return that elements value from your custom function:
function validate(request_type, request_text){
$.post("http://www.example.com/ajax/validate.php",{
type: request_type,
text: request_text},
function(data) {
$('#someElement').text(data);
});
//return the data by getting the value of the html element
return $('#someElement').text();
}
I want to do the following with my javascript codeblock.
Handle all current and new device requests ie. detect,encrypt,decrypt,etc
Return the result to the calling method
Questions
How can I improve the existing code and get rid of the javascript strict warning:anonymous function does not always return a value.
What is the right way of calling my method?
Any help is greatly appreciated
Thanks!
Herewith the code:
This is how I call the current method
//Contents of SmEditor.js
var response = Ext.decode(Prometheus.DeviceRequestHelper.detect(request_id));
//contents of Sm.js
Ext.ns('myApp')
myApp.DeviceRequestHelper = {
detect:function(request_id){
var task = function(){
Ext.Ajax.request({
url: 'device_requests.php',
params:{
action:'get_device', //in php
'request_id':request_id
},
timeout:30000, //30 seconds
success:function(response){//serverside response
var result = Ext.decode(response.responseText); //convert to js objects
if(result.success == true){//device was detected
cons.log('success,device was detected');
cons.log(result);
Ext.TaskMgr.stop(runTask);
return Ext.encode(result); //javascript strict warning
}else{
if(runTask.taskRunCount >= 10){
//retry limit exceeded
Ext.Msg.show({
title:'Server Failure',
msg:"Detection Failed,Unable to detect device",
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
Ext.MessageBox.getDialog().getEl().setStyle('z-index','80000');
Ext.TaskMgr.stop(runTask);
}
}
},
failure:function(response){
Ext.TaskMgr.stop(runTask);
Ext.Msg.show({
title:'Server Failure',
msg:"Failed, server communication error",
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
Ext.MessageBox.getDialog().getEl().setStyle('z-index','80000');
}
})
}
var runTask = {
run: task,
interval:2000,
repeat:10
};
Ext.TaskMgr.start(runTask);
}
}
To prevent this kind of warning, have the function return a value in all cases, or no cases. At the moment you're only returning a value in one if case; the other cases will not return anything. You can even return undefined to make the warning go away. However, what it is telling you is correct: that a function that sometimes has a return value and sometimes doesn't is a bit weird and suggests you're doing something wrong.
What you seem to want to do is have the inner return in the success method return a value from the detect() method. This is absolutely not possible. The inner function can only return a value to the caller of success, which is Prototype itself. By the time this happens, the detect() method has long since returned.
What you have here is asynchronous code. The detect() method can set up an AJAX request, but it must then return immediate to its caller, which will return control to the browser. At some later time, the HTTP request behind the AJAX call will complete, and then the success function will fire. JavaScript cannot call asynchronous code synchronously, or vice versa.
What you have to do is pass a callback function into your method, and then call it back on completion:
Prometheus.DeviceRequestHelper.detect(request_id, function(response) {
// do something with `response`
});
myApp.DeviceRequestHelper= {
detect: function(request_id, callback) {
...
Ext.Ajax.request({
...
success: function(xhr) {
var result= Ext.decode(xhr.responseText);
if (result.success)
callback(result);
...
},
...
});
},
...
};
(I removed the extra Ext.encode->Ext.decode pair, that just seems like a waste of time.)
First, Your detect method will not return a value and will return immediately(even before the ajax call completes) because the ajax call is asynchronous
Second, there's no point returning a value in your success handler. Instead you should provide a callback function to your detect method like so:
Ext.decode(Prometheus.DeviceRequestHelper.detect(request_id, function(response) {
// do something with your response
}));
// detect function takes a callback function as a parameter
myApp.DeviceRequestHelper = {
detect:function(request_id, funCallback){ // pass in a callback function that is
// called when result was a success
var task = function(){
Ext.Ajax.request({
url: 'device_requests.php',
params:{
action:'get_device', //in php
'request_id':request_id
},
timeout:30000, //30 seconds
success:function(response){//serverside response
var result = Ext.decode(response.responseText); //convert to js objects
if(result.success == true){//device was detected
cons.log('success,device was detected');
cons.log(result);
Ext.TaskMgr.stop(runTask);
// return Ext.encode(result); //javascript strict warning
funCallback(Ext.encode(result)); // ===========> callback function called.
}else{
if(runTask.taskRunCount >= 10){
//retry limit exceeded
Ext.Msg.show({
title:'Server Failure',
msg:"Detection Failed,Unable to detect device",
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
Ext.MessageBox.getDialog().getEl().setStyle('z-index','80000');
Ext.TaskMgr.stop(runTask);
}
}
},
failure:function(response){
// ... failure handing code
}
});
}
var runTask = {
run: task,
interval:2000,
repeat:10
};
Ext.TaskMgr.start(runTask);
}
}