In my Angular 1.4.X app, I have some code like this in the controller
function submitForm() {
$scope.submissionInProgress = true;
myService.veryExpensiveOperation();
$scope.submissionInProgress = false;
}
In the view, I try to use $scope.submissionInProgress to show/hide a spinner while veryExpensiveOperation() (a synchronous operation) is in progress.
<div ng-show="submissionInProgress" class="spinner">
Please wait...
</div>
However, what actually happens is that the spinner isn't displayed until veryExpensiveOperation() has almost completed. In other words, it seems there's a delay of a few seconds between when $scope.submissionInProgress = true is called, and when the spinner is actually shown.
Then $http requests are asynchronous. The value of $scope.submissionInProgress gets changed before the request is completed.
If you make an $http request you should better toggle $scope.submissionInProgress inside the resolve function.
e.g.
function submitForm() {
$scope.submissionInProgress = true;
myService.veryExpensiveOperation().then(successCallback, failCallback);
function successCallback() {
//..Do whatever you have to do and then set the $scope on false.
$scope.submissionInProgress = false;
}
function failCallback() {
//Same here
$scope.submissionInProgress = false;
}
}
Solution
I fixed this using $timeout like so:
function submitForm() {
$scope.submissionInProgress = true;
$timeout(function() {
myService.veryExpensiveOperation();
$scope.submissionInProgress = false;
});
}
you can also use callbacks
callback
function submitForm() {
$scope.submissionInProgress = true;
myService.veryExpensiveOperation(function() {
$scope.submissionInProgress = false;
});
}
and the function veryExpensiveOperation:
veryExpensiveOperation = function(callback) {
...
success: function() {
if(typeof callback == "Function") {
callback();
}
}
...
}
Related
I have the following code -
function initialize() {
var defer = $q.defer();
var deferTimer = $q.defer();
var cancelTimeout = $timeout(function() {
if (defer !== null) {
ctrlr.setProcessingParameters('XXX');
defer = ctrlr.openProgressBar();
deferTimer.resolve();
}
}, 1000);
deferTimer.promise.then(function() {
var cancelTimeout2 = $timeout(function() {
if (defer !== null) {
defer.resolve();
ctrlr.setProcessingParameters('Please Wait...');
defer = ctrlr.openProgressBar();
}
}, 4000);
});
//Process Backend service n resolbve defer....
}
// cancel the $timeout service
$rootScope.$on('$destroy', function() {
logger.log("cancelTimeout..timer..");
if (cancelTimeout) {
$timeout.cancel(cancelTimeoutProcess);
cancelTimeout = null;
}
});
// cancel the $timeout service
$rootScope.$on('$destroy', function() {
logger.log("cancelTimeout2..timer..")
if (cancelTimeout2) {
$timeout.cancel(cancelTimeout2);
cancelTimeout2 = null;
}
});
I do not see the loggers print or debugger gets into $destroy. Not sure what's happening here.
$rootScope gets destroyed when you close or leave the page. Everything will be gone then, so there's nothing to clean up at that time.
What you are looking for is $destroy on $scope instead,
$scope.$on('$destroy', function() {
logger.log("cancelTimeout..timer..");
if (cancelTimeout) {
$timeout.cancel(cancelTimeoutProcess);
cancelTimeout = null;
}
});
While in the controller, $scope.$on('$destroy'.. will be called when controller gets destroyed (and not the whole application) with which current $scope is associated.
Let me explain what the trouble is. I have two functions: compute(); and discount_compute();. When the page firsts load both functions get executed once (OK, since discount_compute() is part of compute so it always runs when compute() is executing). When I open the #autobid-panel (it is set on display:none initially) the function discount_compute runs 1 time because of the $('#autobid').on('click', function(), but then it also runs 2 more times because of the '[data-slider]').on('change.fndtn.slider'). Btw everytime this autobid-panel is closed or opened the slider is initialized again. I only want the discount_compute() to run once when #autobid-panel is opened. Any ideas?
function compute() {
//first function
};
function discount_compute() {
//second function
};
$(document).ready(function($) {
$('.price').change(compute).change();
$('#autobid').on('click', function() {
if ($(this).is(':checked')) {
$('#autobid-panel').removeClass("hide");
$(document).foundation('slider', 'reflow');
discount_compute();
} else {
$('#autobid-panel').addClass("hide");
$(document).foundation('slider', 'reflow');
}
});
$('#discount').on('change', function(){
var value = $(this).val();
$('.range-slider').foundation('slider', 'set_value', value);
discount_compute();
});
$('[data-slider]').on('change.fndtn.slider', function(){
discount_compute();
});
});
Thank your for your help!
You don't really explain the reasoning of the data-slider or why you even call discount_compute(); there if you don't want to run it.
One dirty hack you can do is something to this effect:
function compute() {
//first function
};
function discount_compute() {
//second function
};
var harRun=false;
$(document).ready(function($) {
$('.price').change(compute).change();
$('#autobid').on('click', function() {
if ($(this).is(':checked')) {
$('#autobid-panel').removeClass("hide");
$(document).foundation('slider', 'reflow');
if(hasRun != true) {discount_compute(); hasRun=true;}
} else {
$('#autobid-panel').addClass("hide");
$(document).foundation('slider', 'reflow');
}
});
$('#discount').on('change', function(){
var value = $(this).val();
$('.range-slider').foundation('slider', 'set_value', value);
discount_compute();
});
$('[data-slider]').on('change.fndtn.slider', function(){
if(hasRun != true) {discount_compute();}
});
});
In this way, once hasRun is set to true you no longer call discount_compute().
unfortunately $(document).foundation('slider', 'reflow'); fires a change event, so there isn't any nice way.
one way is to off the event before reflow and on straight after:-
function compute() {
//first function
};
function discount_compute() {
//second function
};
$(document).ready(function($) {
$('.price').change(compute).change();
$('#autobid').on('click', function() {
if ($(this).is(':checked')) {
$('#autobid-panel').removeClass("hide");
$('[data-slider]').off('change.fndtn.slider', discount_compute);
$(document).foundation('slider', 'reflow');
$('[data-slider]').on('change.fndtn.slider', discount_compute);
discount_compute();
} else {
$('#autobid-panel').addClass("hide");
$(document).foundation('slider', 'reflow');
}
});
$('#discount').on('change', function(){
var value = $(this).val();
$('.range-slider').foundation('slider', 'set_value', value);
discount_compute();
});
});
I am having issued chaining a bunch of deferred 'then's in my javascript function.
In JQuery 1.7.2 I was able to create something like the following example, passing parameters from each one to determine if I continue.
myAjaxFunction(myParametersObject)
.done(function (noErrors) {
if (anyErrors == true) {
// call ajax routine
return true;
} else {
//stop code execution
return false;
}
})
.then(function (noErrors) {
if (anyErrors == true) {
// call ajax routine
return true;
} else {
//stop code execution
return false;
}
})
.then(function (noErrors) {
if (anyErrors == true) {
// call ajax routine
return true;
} else {
//stop code execution
return false;
}
})
.then(function (noErrors) {
if (anyErrors == true) {
// final code here
}
});
It works perfectly on JQuery 1.7.2 but I am working on a project that requires JQuery 1.11.1 and this no longer works.
How can I pass parameters to the upcoming 'then' in JQuery 1.11.1?
Return jQuery promise value from myAjaxFunction appear to be defined as noErrors at done handler argument parameter
.done(function (noErrors) {
within .done handler as anyErrors ?
if (anyErrors == true) {
similarly at
.then(function (noErrors) {
if (anyErrors == true) {
// call ajax routine
?
Try setting same parameter as argument parameter and within handler , .e.g. anyErrors
var dfd = $.Deferred().resolve(true);
dfd.done(function (anyErrors) {
if (anyErrors == true) {
// call ajax routine
return true;
} else {
//stop code execution
return false;
}
})
.then(function (anyErrors) {
if (anyErrors == true) {
// call ajax routine
return true;
} else {
//stop code execution
return false;
}
})
.then(function (anyErrors) {
if (anyErrors == true) {
// call ajax routine
return true;
} else {
//stop code execution
return false;
}
})
.then(function (anyErrors) {
if (anyErrors == true) {
// final code here
document.body.textContent = anyErrors;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
jsfiddle http://jsfiddle.net/zr5rzb7v/1/
You could use it if you use first then and then done:
var request = $.ajax( url, { dataType: "json" } ),
chained = request.then(function( data ) {
return $.ajax( url2, { data: { user: data.userId } } );
});
chained.done(function( data ) {
// data retrieved from url2 as provided by the first request
});
See official jquery api documentation: http://api.jquery.com/deferred.then/#deferred-then-doneFilter-failFilter-progressFilter
I have defined a div within which a form with default input values is appended based on MySQL table data returned by PHP via an ajax $.get call.
The div looks like:
<div id="esfContainer1">
</div> <!--end esfContainer1 div-->
The div is absolutely positioned relative to the body tag.
The script associated to the form validation broke when it was included on the main page where the call to the form was being made, so I moved it to the PHP output $formContent.
Here is the form validation and submit script included in the PHP output:
<script type="text/javascript">
var senderName = $("#sendName");
var senderEmail = $("#fromemailAddress");
var recipientEmail = $("#toemailAddress");
var emailError = $("#esemailerrorDiv");
senderName.blur(checkName);
senderEmail.blur(checkSEmail);
recipientEmail.blur(checkREmail);
function checkName() {
if (senderName.val() == "YOUR NAME") {
$("#esemailerrorText").html("Please provide your name");
$(emailError).removeClass("esemailError");
$(emailError).addClass("esemailErrorNow");
$(emailError).fadeIn("fast","linear");
$(emailError).delay(2000).fadeOut("slow","linear");
return false;
} else {
return true;
}
};
function checkSEmail() {
var a = senderEmail.val();
var filter = /^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
if (filter.test(a)) {
return true;
} else {
$("#esemailerrorText").html("Please enter a valid email address");
$(emailError).removeClass("esemailError");
$(emailError).addClass("esemailErrorNow");
$(emailError).fadeIn("fast","linear");
$(emailError).delay(2000).fadeOut("slow","linear");
return false;
}
};
function checkREmail() {
var a = recipientEmail.val();
var filter = /^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
if (filter.test(a)) {
return true;
} else {
$("#esemailerrorText").html("Your friend\'s email is invalid");
$(emailError).removeClass("esemailError");
$(emailError).addClass("esemailErrorNow");
$(emailError).fadeIn("fast","linear");
$(emailError).delay(2000).fadeOut("slow","linear");
return false;
}
};
$("#emailForm").submit (function() {
if (checkName() && checkSEmail() && checkREmail()) {
var emailerData = $("#emailForm").serialize();
$.get("style.php",emailerData,processEmailer).error("ouch");
function processEmailer(data) {
if (data=="fail") {
return false;
} else if (data=="pass") {
$("#c1Wrapper").fadeOut("slow","linear");
$("#confirmation").fadeIn("slow","linear");
$("#esfContainer1").delay(2000).fadeOut("slow","linear");
$("#backgroundOpacity").delay(2000).fadeOut("slow","linear");
return false;
}
};
return false;
};
return false;
});
I have splatter-bombed the above submit function with "return false;" because the submit function has been simply opening the processing PHP script rather than executing the $.get. Watching the submit function with Firebug reports that processEmailer is undefined.
I am very new to this. I was assuming that because the ajax callback is being defined within the submit function (and that the processEmailer function is defined directly below the ajax call) that there wouldn't be a problem with definition.
Thanks in advance for any help.
You've been trapped by function statements. Function declarations (which would be hoisted) are not allowed inside blocks (if/else/for bodies) and if they are appear there, behaviour is not defined. Firefox defines them conditionally, and in your case after you've used it in the $.get call - where it was undefined then - like in var functionName = function() {} vs function functionName() {}.
To solve this, simple put it outside the if-block (or even outside the whole callback). Btw, .error("ouch") won't work, you need to pass a function.
$("#emailForm").submit (function() {
if (checkName() && checkSEmail() && checkREmail()) {
var emailerData = $("#emailForm").serialize();
$.get("style.php",emailerData).done(processEmailer).fail(function() {
console.log("ouch");
});
}
return false;
// now a proper function declaration, will be hoisted:
function processEmailer(data) {
if (data=="fail") {
return false;
} else if (data=="pass") {
$("#c1Wrapper").fadeOut("slow","linear");
$("#confirmation").fadeIn("slow","linear");
$("#esfContainer1").delay(2000).fadeOut("slow","linear");
$("#backgroundOpacity").delay(2000).fadeOut("slow","linear");
return false;
}
}
});
I'm new to JavaScript and I'm having problems with this script.
it's part of a web game and the script is suppose to refresh the page until the player wins or loses.
for some reason it doesn't stop refreshing, I put an alert function to check if the functions works, and i get the alerts but it's still continue refreshing the page.
what am i doing wrong?
var t;
$(document).ready(function () {
intervals();
});
function intervals() {
t = self.setInterval('refreshData()', 10000);
}
function youWin() {
var f = $('#status:contains("YOU ARE THE WINNER!")');
if (f.length > 0) {
alert("YOU ARE THE WINNER!");
t = clearInterval(t);
}
}
function youlose() {
var f = $('#status:contains("You lost!")');
if (f.length > 0) {
alert("You lost!");
t = clearInterval(t);
}
}
function refreshData() {
$('#ajaxGame').load('RefreshCurrentPlayerServlet #ajaxGame');
youWin();
youlose();
}
You need to fix the reference to self and fix the .load() call.
.load() is asynchronous so it does not complete before you call youWin() and youLose() right after it. You need a completion function so you can check winning or losing after the .load() completes successfully.
refreshData() should be structured like this:
function refreshData() {
$('#ajaxGame').load('RefreshCurrentPlayerServlet #ajaxGame', function() {
youWin();
youlose();
});
}
You also should change this:
t= self.setInterval('refreshData()',10000);
to this:
t = window.setInterval(refreshData, 10000);
I don't see that self was even defined so that could have also been causing your problem and you should use the function reference directly rather than put in a string.
And, as a cleanup issue, you should change both occurences of this:
t = clearInterval(t);
to this:
clearInterval(t);
Here's a cleaned up version of the code that also eliminates global variables and unnecessary function definitions:
$(document).ready(function() {
var t = window.setInterval(function() {
$('#ajaxGame').load('RefreshCurrentPlayerServlet #ajaxGame', function() {
youWin();
youlose();
});
}, 10000);
function youWin() {
if ($('#status:contains("YOU ARE THE WINNER!")').length) {
alert("YOU ARE THE WINNER!");
clearInterval(t);
}
}
function youlose() {
if ($('#status:contains("You lost!")').length) {
alert("You lost!");
clearInterval(t);
}
}
});