We are putting together an FSSO API that requires a popup window for the user to log in. In the popup, we perform two tasks:
Calling a service to populate a profile values, then setting a page
to redirect the user to based on the event type (login or registration).
Redirecting the user to the redirect page in the parent window and closing the FSSO popup.
Code:
$(document).ready(function() {
var nextPage = "index.html",
storage = window.opener.sessionStorage;
function setStorage(callback){
$.ajax({
type: "GET",
cache: false,
dataType: "json",
url: "https://someserviceURL/service/profile",
success: function(objJSON){
//storage.op is set on the parent page when login or reg link is clicked
if (storage.op == "login") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true)); //Some browsers don't support booleans in sessionStorage
nextPage = "dashboard.html";
}
else if (storage.op == "register") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true));
nextPage = "options.html";
}
},
error: function( jqXHR, textStatus, errorThrown){
//display error message
}
});
callback();
}
setStorage(function(){
if (typeof window.opener != "undefined" && window.opener != null){
setTimeout(function(){
window.opener.location.href = nextPage;
window.close();
},3000);
}
});
});
Problem:
The window seems to be closing before I'm able to set the sessionStorage values if I set the timeout to anything less than 3000. I just want to close the window in response to those values being set, not some arbitrary amount of time passing. I tried the trick of setting the timeout to 0 but no luck, and I tried just the callback with no timeout.
Looking for best practices here on handling timing issues like these, what I have now feels hacky. :)
The call to $.ajax() is asynchronous, which means that the rest of the script will continue to execute immediately after the call is made, without waiting for the call to complete and trigger the success or error handlers.
This means that your function called callback is being executed before your success handler. It may usually work as intended with a 3000ms timeout as your web service often takes less time than this to complete, so your callbacks are executed first in these cases. As you mentioned, this is not a reliable way to control the order of events.
One solution would be to execute your callback as part of the complete handler, as follows:
$(document).ready(function() {
var nextPage = "index.html",
storage = window.opener.sessionStorage;
function setStorage(callback){
$.ajax({
type: "GET",
cache: false,
dataType: "json",
url: "https://someserviceURL/service/profile",
success: function(objJSON){
//storage.op is set on the parent page when login or reg link is clicked
if (storage.op == "login") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true)); //Some browsers don't support booleans in sessionStorage
nextPage = "dashboard.html";
}
else if (storage.op == "register") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true));
nextPage = "options.html";
}
},
error: function( jqXHR, textStatus, errorThrown){
//display error message
},
complete: function( jqXHR, textStatus){
callback();
}
});
}
setStorage(function(){
if (typeof window.opener != "undefined" && window.opener != null){
window.opener.location.href = nextPage;
window.close();
}
});
});
Or if you don't care about the returned arguments you could pass callback directly to complete. Note that complete will be executed in both success and error conditions, so you might want to call callback only in your success handler and do something else with any errors.
You should call your callback as the last action of your success function:
.
.
.
else if (storage.op == "register") {
storage.firstname = objJSON.firstName;
storage.lastname = objJSON.lastName;
storage.setItem('loggedIn',JSON.stringify(true));
nextPage = "options.html";
}
callback(); // execute your callback to the window closer here.
},
error: function( jqXHR, textStatus, errorThrown){
//display error message
}
});
}
Related
Hello I have this script that moves from one page through a href without page load.
It works perfectly, but I want to redirect to the requested page if Ajax takes longer than 5 seconds to respond, usually caused by probably slow internet connection.
In this case: Stop the script and load the page normally.
Firstly the href:
new
New 1
This is the script:
<script>
$(function(){
$("a[rel='tab']").click(function(e){
pageurl = $(this).attr('href'); //get the href clicked
$.ajax({url:pageurl+'?rel=tab',
success: function(data){
$('#mole').html(data);
}
});
if(pageurl!=window.location){
window.history.pushState({
path:pageurl
},'',pageurl);
}
return false;
});
});
$(window).bind('popstate', function(){
$.ajax({
url:location.pathname+'?rel=tab',
success: function(data){
// here how do I detect if the success takes longer than 5 seconds
// stop the script and load the page normally which is the URL parameter in ajax
$('#mole').html(data);
}
});
});
</script>
First, we need to add a timeout to the ajax handler so it will cancel the request after 5 seconds (here we use 5000 for milliseconds). Then, based on the docs, head to error and you can see the second param is textStatus. If it was a timeout, this will be equal to "timeout". This is probably your easiest path to what you need to do. Update the error handler as needed for your functionality.
$(window).bind('popstate', function() {
var url = location.pathname + '?rel=tab';
$.ajax({
timeout: 5000,
url: url,
success: function(data) {
$('#mole').html(data);
},
error: function(jqXHR, textStatus) {
if (textStatus === 'timeout') {
// we know the ajax failed due to a timeout,
// do what you need here, below is an example.
window.location = url;
}
}
});
});
I've created a controller in Magento which check whether or not there are products in a list. If there are products in list it will return true otherwise false.
Here is the front-end which triggers the ajax call, bare in mind I can not change this to be a form. It has to be a link.
Compare Products
Here is the ajax call.
jQuery(".compare-product-link").on("click", function(e) {
jQuery.ajax({
async : false,
dataType : "json",
url : "/compareextra/compare/allowed",
success : function(data) {
//console.log(data);
if(data.isAllowed != true) {
e.preventDefault();
}
}
});
});
The problem I have is that the async is deprecated and is not good for user experience, saying that there are many answer out there which add a delay of 3 seconds, I also don't want that because thats not good for user experience.
I've also tried using a promise call but it only works with async : false.
jQuery(".compare-product-link").on("click", function(e) {
var response = false;
jQuery.ajax({
dataType : "json",
url : "/compareextra/compare/allowed",
success : function(data) {
console.log(data);
if(data.isAllowed) {
response = true;
}
}
}).done(function (){
console.log(response);
if(response != true) {
e.preventDefault();
}
});
});
EDIT
Another problem I also have is if I store the link into a variable and then open a new window as so window.location = href; most browser will block it and users will have to manually accept pop ups from the target site, which again is not good for user experience.
you cannot really achieve this using preventDefault like you said - because of async.
what I would try is:
preventDefault
store href as a variable
call ajax
redirect to href variable if true and not if false
jQuery(".compare-product-link").on("click", function(e) {
var href = $(this).attr('href');
e.preventDefault();
jQuery.ajax({
async : false,
dataType : "json",
url : "/compareextra/compare/allowed",
success : function(data) {
//console.log(data);
if(data.isAllowed == true) {
window.location = href;
}
}
});
});
if you need to create a link action you can use this code:
function triggerClick(url){
$('body').append('<span id="click_me_js"></span>');
$('span#click_me_js a')[0].click();
$('span#click_me_js').remove();
}
which will mimic a regular click on <a>
I have a website where users can work on projects and their work gets automatically saved to my database. Every couple seconds on my site an ajax (post) call occurs (usually in jquery) to check permissions and what not.
With one section of code is there any way so check if any of the ajax calls on your page fail. I don't want to have to go to every individual call and add a block of code to the end.
Basically this is so I can alert the user if they have lost connection or if something is going wrong.
You can use the jQuery event ajaxError. It will be triggered whenever an Ajax request completes with an error:
$(document).ajaxError(function() {
console.error('Error');
});
Check out the documentation.
$(document).ready(function(){
//ajax setup settings
$.ajaxSetup ({
cache: false,
async: false,
statusCode: {
404: function() {
alert('Page not found!');
},
500: function(jqXHR, textStatus) {
alert('Server side: ' + textStatus);
}
}
});
});
I hope this may help you
I would suggest you to override the original jquery ajax function.
var $_ajax = $.ajax; // reference to original ajax
$.ajax = function(options) {
if (options.error) {
// reference to original error callback
var originalErrorHandler = options.error;
var errorHandlerContext = options.context ? options.context : $;
var customErrorHandler = function(xhr, status, error) {
// notify error to your user here
};
// override error callback with custom implementation
options.error = customErrorHandler;
};
return $_ajax.apply($, arguments);
}
I am using chrome.
I have an iframe in which i require to hit a url that supports jsonp.
so i used this code :
$.ajax({
dataType: 'jsonp',
url: my_url.endpoint + '/login/v1/token' ,
data: form_to_object("#signin_form"),
context: window,
// All Ajax calls to ABC are json
// Response statuses other than 200 are caught in a timeout
timeout: 10000, //10s
// Handler for successful calls to ABC: calls that return with statusCode 200
success: function(data, textStatus, jqXHR) {
// console.log(data);
alert("in access_token success");
if (data.hasOwnProperty('error_flag')) {
// Errors associated with this action are caught here:
// invalid_credentials, account_lockout, etc.
if (data.hasOwnProperty("jump")) {
ABC_show_frame(data.jump);
} else {
ABC_error_handler(data);
}
return;
}
// Auth succeeded, we can log in the user
GetUserProfile(data);
// ABC_success_handler(data);
},
error: function(data, textStatus, jqXHR) {
alert("In access_token error");
if (data.hasOwnProperty("jump")) {
ABC_show_frame(data.jump);
} else {
ABC_error_handler(data);
}
}
});
Now this code does not attach a callback=some_random_function_name in the url that it generates after attaching the parameters of data.
like https://abc/login/v1/token?username=ashish?password=abc but no callback.
When i debug it line by line, it do call the url with callback=something, and it seems to work. (seems because may be sometime it does not attach even in debugging line by line.)
But when i just run it, it does not.
I think that may be the problem is a bug in jquery where it also has to attach data that it got from form_to_object() and may be that overrides the callback parameter. But that is just a guess.
What should i do ?
I had a form and i was writing my own custom function that would be called when submit button of the form was clicked. In that function i was not stopping the event from propagating further. This lead to this weird errors.
$("form.ims.ajax").submit(function(event) {
event.preventDefault();
// do your stuff
});
This solved the problem.
I'm trying to fetch posts dynamically using AJAX and JQuery by checking if the user is close to the bottom. Serverside is in python on GAE.
Listening for scroll:
this.config.window.on('scroll',this.loadContent);
1.Checking for distance from bottom
2.Sending an ajax request with the number of current posts in order to retrieve the next 10
3.results.check = true means that the server has no further posts to send.
loadContent: function(){
// 1
if($(document).height() - $(window).height() - $(window).scrollTop() < 1000) {
var posts = $('.troll').children('div').length;
data = 'loadmore=True&offset=' + posts; //2
$.ajax({
url: '/',
type: 'POST',
data: data,
dataType: 'json',
success: function(results){
if (results.check === 'true'){ //3
$(window).unbind('scroll');
return;
}
Post.insert10Values(results);
}
});
};
},
insert10Values: function(results){
var update = Handlebars.compile($('#troll10').html()),
troll10update = update(results);
$('div.troll').append( troll10update );
}
The problem here is that when scrolling fast, two or more requests are sent to the server and i get duplicate entries. I want to rate-limit on client-side.
Set a flag loading = false. Before you send a request, check the flag. If it's false, set the flag to true and proceed with request, otherwise ignore the event. When results arrive, show them and set the flag back to false.
Part of your problem is scroll event will trigger many times a second
you can throttle any function calls doing something like this:
var scrollTimer=false;
var delay=500; /* 1/2 second*/
$(window).on('scroll',function(){
if( scrollTimer){
clearTimeout( scrollTimer);
}
scrollTimer=setTimeout(function(){
/* run your code here*/
}, delay);
});
As for the ajax you could store a time for last ajax call and set a miniumum difference based on now vs stored time before making a new ajax call
var lastAJAX=Date.now(), AJAXMin=5000;/* 5 seconds*/
function checkAJAXCalls(){
var now=Date.now(), diff=now-lastAJAX;
if( diff >= AJAXMin){
lastAJAX=now;
return true;
}else{
return false;
}
}
Then run if(checkAJAXCalls()) prior to making request. Concept could be modified to update lastAJAX in success callback of $.ajax also
jQuery.ajax has a method called beforeSend. It is executed right before your ajax call. You can use it to check if any other request is in progress and cancel the call if there is one. If you return false in beforeSend function, the ajax call will not be fired so you won't have any duplicate content.
$.ajax({
url: '/',
type: 'POST',
data: data,
dataType: 'json',
beforeSend: function() {
if (window.nextPageProcess) {
return false;
} else {
window.nextPageProcess = 1;
}
},
success: function(results){
if (results.check === 'true'){ //3
$(window).unbind('scroll');
return;
}
Post.insert10Values(results);
window.nextPageProcess = 1;
}
});