setTimeout doesn't start until I stop the page - javascript

On the event of a form submit, I have a jquery ajax function that queries for the amount of data uploaded from a file that I can use in making a progress bar. This is my js file:
var submitted = '';
var counter = 0;
$('#webfileuploadform').submit(function(){
if(submitted == 'submitted') return false; // prevents multiple submits
var freq = 1000; // freqency of update in ms
var progress_url = '/upload_progress/' + $('#id_progress_id').val(); // ajax view serving progress info
update_progress_bar();
function update_progress_bar(){
$.ajax({
dataType:"json",
url: progress_url,
success: function(data){
counter ++
console.log('hit success ' + counter);
},
complete: function(jqXHR, status){
if (status == 'success'){
console.log('hit complete', status == success')
} else {
console.lot('hit complete', status == something else')
}
}
});
setTimeout(function(){update_progress_bar()}, freq);
}
submitted = 'submitted'; // marks form as submitted
});
So, the user will use the form to select the file then click submit. This all happens just fine. The form submits, the file is uploaded, and the progress shows just fine when I open my ajax view in a separate tab and keep refreshing it. What I don't understand is why the update_progress_bar function runs once until I stop the page. I'll upload a file but until I click the 'x' on my browser I won't get any of the 'hit success' in my console. After I hit stop, the console spits out the 'hit success ' plus the counter. But I shouldn't have to hit stop to do this. I need this to work when the file is uploading.
Any ideas what my problem is?
**
EDIT
**
Not sure if it makes a difference, but I am doing all this on a django development server. Which I think shouldn't have a problem with two XMLHTTPRequests in the same session.

Try this:
var $submitted = '';
$('#webfileuploadform').submit(function(){
if($submitted == 'submitted') return false; // prevents multiple submits
var freq = 1000; // freqency of update in ms
update_progress_bar();
function update_progress_bar(){
var progress_url = '/upload_progress/' + $('#id_progress_id').val(); // ajax view serving progress info
$.ajax({
dataType:"json",
url: progress_url,
success: function(data){
console.log("hit success");
},
complete:function(jqXHR, status) {
if (status == 'success') {
setTimeout(update_progress_bar, freq);
}
}
});
}
$submitted = 'submitted'; // marks form as submitted
});

As it stands, the code looks good to me. I would add some more console logging to make sure the ajax call is returning with the status you expect:
complete:function(jqXHR, status) {
console.log('got to complete, status: ', status);
if (status == 'success') {
console.log('status is success, calling setTimeout.');
setTimeout(update_progress_bar, freq);
}
}

In the ajax function I set the async setting to false:
$.ajax({
async: false,
...
});
This has solved the problem. I have no idea how. From what I understand, this setting forces the ajax request to wait until all other http requests are finished before allowing it to continue. With that being the case, I have no idea how this works. I'll keep from answering my own question in case someone knows why this is working this way...

Related

AJAX query not always updating information consistently

I am experiecing some issues with AJAX updating the page. The actual data in the database is updated but this is not always reflecting in real time on the web page.
For example, I have the following event:
$("#add_note").click(function(e) {
//e.preventDefault();
$("#add_note_form").validate({
rules: {
contact_note: {
required: true
}
},
submitHandler: function(form) {
contact.modal_update({
'obj' : $('#add_note_form'),
'uri' : '/contact/add_note/'
});
}
});
});
This function when a new note is created calls a callback to validate the form fields first and then if successful calls a callback inside a seperate class to conduct the update. See the modal_update class below:
// Update modal
this.modal_update = function(data)
{//
// Declare a few variables for the data object we've received
obj = data.obj // The form element to serialize
uri = data.uri;
// Get the form ID from the data-target attribute
id = obj.attr('data-target');
// URL to send to
url = this.site_url + uri + id;
// The form object
this.post_data(obj.serialize(),url);
// Hide Modal
obj.closest('.modal').modal('hide');
// Refresh
this.refresh();
}
This then figures out the correct route to ajax and calls a ajax call back inside the same class:
// AJAX post
this.post_data = function(obj,uri)
{
$.ajax({
data: obj,
dataType: 'json',
type: 'post',
url: uri,
headers: { "cache-control": "no-cache" },
cache: false,
success: function (response) {
if (response.success == true)
{
$("#alert_success .msg").html(response.message);
$("#alert_success").fadeIn(200).delay(2000).fadeOut(200);
}
else
{
$("#alert_error .msg").html(response.error);
$("#alert_error").fadeIn(200).delay(2000).fadeOut(200);
console.log(response.error);
}
}
});
}
I am then running another class callback to "refresh" the data in all the elements on the page:
this.refresh = function()
{
// Refresh the ajax requests
this.get_contact_data();
this.get_notes();
this.get_contact_log();
this.get_contact_tasks();
}
This class re loads the functions which run on page load to get the inial data into the tables/fields on the page. See "get_notes" below:
// Get notes
this.get_notes = function()
{
// Get all notes and populate table
var log_uri = this.site_url + "/contact/get_notes/" + this.contact_id;
this.get_data(log_uri,function(data) {
notes = $("#contact_notes ul");
notes.empty("");
// Populate the contact fields, assuming there is a result to play with
if (data != false) {
//alert(JSON.stringify(data));
$("#notes-tab .count").html("(" + data.length + ")");
$.each( data, function( key, value ) {
notes.append("<li class='list-group-item' modal-id='editNoteModal' data-target='" + value.ID + "'><div class='row'><div class='col-lg-3'><i class='fa fa-sticky-note mr-3'></i>" + value.timestamp + "</div><div class='col-lg-7'>" + value.note + "</div><div class='col-lg-2'><a href='#' class='edit mr-3'><i class='fa fa-edit mr-1'></i>Edit</a><a href='#' class='delete'><i class='fa fa-times mr-1'></i>Remove</a></div></div></li>");
});
console.log('Notes loaded');
} else {
notes.append("<li>There are currently no notes for this contact</li>");
}
});
}
Now the problem:
For some reason this does not update consistently in real time. The data is updated fine on the server side but on the client side the update/refresh does not always update. I might add a note and get a correct update response but the refresh method seems to be receiving the old data and always be one note behind. So the next time I add a note, the one I added before then appears and so forth.
Another problem I am experiencing is the methods seem to stack on each event so if I add one note (or one of the other methods) I will see the console say "notes loaded" but on the second note it says "notes loaded" twice, then on the 3rd note added 3 times and so forth.
I am sure there must be something fatal flaw in the design of my code here but I am not experienced enough with javascript/jquery to notice what direction I am going wrong so I can fix it.
I thought that this was an issue with ajax caching and not refreshing the result so I have adjusted the ajax request as cache none and also to send no cache headers. I am running in wamp.
In your case, your refresh code will always run before your data got updated. Because ajax is asynchronous so the code behind and below ajax will always execute nearly the time your ajax running.
At the time you run your post_data function to call the API, the refresh function got run too. So it's done before your data got updated.
You should run refresh function inside ajax callback. For example:
this.post_data = function(obj,uri, callback)
{
$.ajax({
data: obj,
dataType: 'json',
type: 'post',
url: uri,
headers: { "cache-control": "no-cache" },
cache: false,
success: function (response) {
if (response.success == true)
{
$("#alert_success .msg").html(response.message);
$("#alert_success").fadeIn(200).delay(2000).fadeOut(200);
}
else
{
$("#alert_error .msg").html(response.error);
$("#alert_error").fadeIn(200).delay(2000).fadeOut(200);
console.log(response.error);
}
callback();
}
});
}
And in modal_update, you pass refresh function to post_data as a callback:
this.modal_update = function(data)
{//
// Declare a few variables for the data object we've received
obj = data.obj // The form element to serialize
uri = data.uri;
// Get the form ID from the data-target attribute
id = obj.attr('data-target');
// URL to send to
url = this.site_url + uri + id;
// The form object
this.post_data(obj.serialize(),url, this.refresh);
// Hide Modal
obj.closest('.modal').modal('hide');
}
You should read more about asynchronous ajax. You can use other tricky solution is setTimeout to run this.refresh but I do not recommend that because you not sure when the update is done.

Is there a time lag in the YouTube Data API 3 rate endpoint?

I have an application that displays a YouTube video and has a rate button to allow a user to like or unlike the video. On the click event 3 functions are called chained together through the success function of the ajax. The flow is this: ytvRate() -> getRating() -> showRating()
When I log the actions and results the response from getRating() does not have the value that I sent in ytvRate(). If I wait a while and refresh the page, the result of getRating() comes back correct. I call getRating() inside the success function of the ajax in ytvRate(). Doesn't that mean the function should not be called until a success response is received?
Here is an example of my logs:
rating sent: like
call get rating
this is my rating: none
call show rating
As you can see, the rating returned from the API is not correct - it should be the rating I just sent. Upon refresh the same call does return the correct rating... so, is there a delay or something to the data api updating the correct information? How can I get the correct rating on the same button click that sends the request?
Here are the functions (showRating does not seem relevant to the problem. It works fine as long as it gets the correct rating - which it is not.)
function ytvRate(id, rating, event){
event.preventDefault()
var apiKey = 'This is a valid key';
var client_id = 'This is a valid client id';
var redirect_uri = 'This is a redirect uri';
var scope = 'https://www.googleapis.com/auth/youtube';
var rateUrl = 'https://www.googleapis.com/youtube/v3/videos/rate?id='+id+'&key='+apiKey+'&rating='+rating;
if(getHash().access_token){
var token = getHash().access_token;
$.ajax({
type: "POST",
url: rateUrl,
beforeSend: function (request){
request.setRequestHeader('Authorization', 'Bearer ' + token);
},
success: function(data){
console.log('rating sent: '+rating);
getRating(id);
},
error: function(e) {
console.log(e);
}
});
} else{
window.location = 'https://accounts.google.com/o/oauth2/v2/auth?client_id='+client_id+'&redirect_uri='+redirect_uri+'&scope='+scope+'&response_type=token&prompt=consent&include_granted_scopes=false';
}
return false;
}
function getRating(id){
var getRatingUrl = 'https://www.googleapis.com/youtube/v3/videos/getRating?id='+id;
console.log('call get rating');
if(getHash().access_token){
var token = getHash().access_token;
$.ajax({
type: "GET",
url: getRatingUrl,
beforeSend: function (request){
request.setRequestHeader('Authorization', 'Bearer ' + token);
},
success: function(data){
var rating = data.items[0].rating;
console.log("this is my rating: " + rating);
showRating(rating, id);
}
});
}
}
function showRating(response, id){
console.log('call show rating');
numLikes(id);
if(response == 'like'){
document.getElementById("notliked").className = "hide";
document.getElementById("liked").className = "";
document.getElementById("like-btn").style.color = "#87CEFA";
document.getElementById("like-btn").setAttribute("onclick", "ytvRate('"+id+"', 'none', event)");
} else{
document.getElementById("notliked").className = "";
document.getElementById("liked").className = "hide";
document.getElementById("like-btn").style.color = "inherit";
document.getElementById("like-btn").setAttribute("onclick", "ytvRate('"+id+"', 'like', event)");
}
}
Edit:
Interestingly, if I call the youtube/v3/videos resource in a new method instead of youtube/v3/videos/getRating and access the statistics.likeCount, the number is instantly updated. Why can I not receive the user rating with the same efficiency?
After the discussion in the comments I suggest you to take a different approach. When ytvRate success you don't need to fetch getRating as you already know what is the rating set by the user.
The rate method is like a setter in regular programming language - if it successed (didn't throw an exception or returned an error) you can assume the current value is the one you set without fetching it again. This might be wrong assumption in multithreaded/distributed enviroments but might be ok in your case.
function ytvRate(id, rating, event){
...
success: function(data){
console.log('rating sent: '+rating);
showRating(rating, id);
}
...
}

Detecting if a user has logged out (got no session) using php via JS, and if so - stopping the execution of the remainder of the JS function

I am trying to find a way to detect if a user has no session (which can happen in 2 ways - if the sessions expired OR if the user logged out in another browser window/tab) while running specific JS/jQuery functions.
If the user has no session, the function should stop executing at that point (return false).
I tried using AJAX like so:
function CheckForSession() {
var str="chksession=true";
jQuery.ajax({
type: "POST",
url: "chk_session.php",
data: str,
cache: false,
success: function(res){
if(res == "0") {
alert('Your session has been expired!');
}
}
});
}
chk_session.php is
require('includes/application_top.php');
$session_test = $_SESSION['customer_id'];
if($session_test == '') {
//session expired
echo "0";
} else {
//session not expired
echo "1";
}
Then I am calling that function inside:
jQuery('body').on('click','.cart_icon_div1.active.selected', function(){
CheckForSession();
//if the session doesn't exist, stop running this function, else continue to do more cool stuff
});
The problem is that I am unable to get this to work. Frankly, my js/jQuery skills are quite limited.
Any help is greatly appreciated.
Here's a callbacky version:
function CheckForSession(onLoggedIn, onLoginExpired) {
var str="chksession=true";
jQuery.ajax({
type: "POST",
url: "chk_session.php",
data: str,
cache: false,
success: function(res){
if(res == "0") {
onLoginExpired();
} else {
onLoggedIn();
}
}
});
}
jQuery('body').on('click','.cart_icon_div1.active.selected', function(){
CheckForSession(function() {
// Do any important session-required stuff here
},
function() {
alert('Your session has been expired!');
});
});
A couple things you could do:
Check login status on page load and have the status ready to go when the user clicks
pass a callback into CheckForSession which it runs when the server gives a response.
Personally I would go with option 1.

submit form with ajax validation jquery / standard javascript

I'll start with an apology - I'm a .NET coder with little (no) front-end experience.
When the user clicks on Submit, the form needs to call a REST service, if the service returns true then the user is presented with a warning that a duplicate exists and are asked whether they want to continue. Appreciate any help.
I have the Submit button ONCLICK wired up to Approve()
When the checkForDuplicateInvoice() gets called, it passes the control back to the calling function right away before the ajax call has a chance to get the result. The effect is that the Validate() function finishes without taking into account whether or not a duplicate invoice exists.
I need help in modifying the form so that when the user clicks on the submit button, the form validates (including the ajax call to the db) before finally submitting.
I've modified the code based on Jasen's feedback.
I'm including https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js in my header.
The error I get now is "Object doesn't support property or method 'button'"
What I have now for my form submission/validation is:
$(document).ready(function () {
$("#process").button().click( function () {
if (ValidateFields()) { // internal validation
var companyCode = document.getElementById("_1_1_21_1").value;
var invoiceNo = document.getElementById("_1_1_25_1").value;
var vendorNo = document.getElementById("_1_1_24_1").value;
if (vendorNo == "undefined" || invoiceNo == "undefined" || companyCode == "undefined") {
return false;
}
$.ajax({ // external validation
type: "GET",
contentType: "application/json;charset=utf-8",
//context: $form,
async: false,
dataType: "jsonp",
crossDomain: true,
cache: true,
url: "http://cdmstage.domain.com/services/rest/restservice.svc/CheckDuplicateInvoice?InvoiceNumber=" + invoiceNo + "&VendorNumber=" + vendorNo + "&CompanyCode=" + companyCode,
success: function (data) {
var result = data;
var exists = result.CheckForInvoiceDuplicateResult.InvoiceExists;
var valid = false;
if (exists) {
if (confirm('Duplicate Invoice Found! Click OK to override or Cancel to return to the form.')) {
valid = true;
}
}
else {
valid = true; // no duplicate found - form is valid
}
if (valid) {
document.getElementById("_1_1_20_1").value = "Approve";
doFormSubmit(document.myForm);
}
},
error: function (xhr) {
alert(xhr.responseText);
}
});
}
});
});
First review How do I return the response from an asynchronous call? Understand why you can't return a value from the ajax callback functions.
Next, disassociate the submit button from the form to prevent it from performing default submission. Test it to see it does nothing.
<form>
...
<button type="button" id="process" />
</form>
Then wire it up to make your validation request
$("#process").on("click", function() {
if (valid()) {
$(this).prop("disabled", true); // disable the button to prevent extra user clicks
// make ajax server-side validation request
}
});
Then you can make your AJAX request truly asynchronous.
$.ajax({
async: true,
...,
success: function(result) {
if (exists) {
// return true; // returning a value is futile
// make ajax AddInvoice call
}
}
});
Pseudo-code for this process
if (client-side is valid) {
server-side validation: {
on response: if (server-side is valid) {
AddInvoice: {
on response: if (successful) {
form.submit()
}
}
}
}
}
In the callback for the server-side validation you make the AddInvoice request.
In the callback for AddInvoice you call your form.submit().
In this way you nest ajax calls and wait for each response. If any fail, make the appropriate UI prompt and re-enable the button. Otherwise, you don't automatically submit the form until both ajax calls succeed and you call submit() programmatically.

jQuery form submission is returning false instead of waiting for server response

I have a jQuery form which sends data to my server after validating. The server sends a response and then my site processes the returned information. Here's the javascript in question:
$("#signup").on('submit', function(e){
var isvalidate = $("#signup").valid();
if(isvalidate)
{
var json = $('#signup').serialize();
var theEmail = $('#signup #inputEmail').val();
//Hide step 1 modal
$('#stepOneSignup').modal('hide');
//Open processing modal
$('#processingModal').modal();
$.post('http://localhost:5000/agm/saveNewUser/', json, function( data ) {
alert('returned some data');
if(data === "Success"){
//Hide processing modal
$('#processingModal').modal('hide');
}else if(data === "UserExists"){
$('#processingModal').modal('hide');
$('#duplicateEmail').modal();
}
});
}else{
return false;
}
});
I can see in my server log that the $.post(http://localhost...) is posting just fine. My server is processing the form information (which takes a couple of seconds) but before it gets a chance to send a response my form redirects my browser to:
http://localhost:8888/?firtName=whateverinfoIputintheform&lastName=yeahyeahyeah
I never get an alert('returned some data');.
Any idea what's going on here?
I think your ajax call is asynchronous. Try doing something link this:
Replace your ajax call with this:
$.ajax({
type: "POST",
url: 'http://localhost:5000/agm/saveNewUser/',
data: json,
dataType: 'text',
async: 'false',
success: function(){
alert('returned some data');
if(data === "Success"){
//Hide processing modal
$('#processingModal').modal('hide');
}else if(data === "UserExists"){
$('#processingModal').modal('hide');
$('#duplicateEmail').modal();
}
}
});
and then
return true;
You should return false, in any case, to prevent the default post-back:
$("#signup").on('submit', function(e){
var isvalidate = $("#signup").valid();
if(isvalidate)
{
...
}
return false;
});
It is causing the ajax post request but can not process it's response because the default post-back causes before the ajax post's response is received.
$.post() is an ajax method. The browser will not wait for the response and will keep on working as if the a server request was never made.
$.post('http://localhost:5000/agm/saveNewUser/', json, function( data ) {
...
}).done(function(){
return true;
});

Categories

Resources