I have an object literal which represents a page structure of an app. If a certain condition is met, I want to skip a page and go to the next one.
Please take a look at the following code:
var someObj = {
"page-1": {
before: function(x){
switch(x){
case true:
break;
case false:
someObj['page-2'].init(); // HERE, I want to skip to the page-2 init() method, **and not go back!**
return false;
break;
}
},
init: function(){
this.before(false);
alert("This is page 1!"); // I don't want to see this!
},
},
"page-2": {
init: function(){
alert("this is page 2!"); // After this alert, I want to STOP, and not go back to the page-1 init() method!
return false;
},
},
}
someObj['page-1'].init();
I can't work out how to stop after the "This is page 2!" alert - I always get 2 alerts. None of the return false work? How can I do this?
Change before() to always return a boolean and make the remainder of the page-1 init() method conditional:
before: function(x){
if (!x) {
someObj['page-2'].init();
}
return x;
},
init: function(){
if (this.before(false)) {
alert("This is page 1!");
}
},
You can either have all your functions pay attention to return values from other functions they call, implementing an "abort" protocol of some kind, or you can throw an exception.
"page-2": {
init: function(){
alert("this is page 2!");
throw new Error("Cease and desist");
},
},
The choice between the two approaches depends on your needs.
What I would do is actually check to see what returns from your before() function and based on that value execute the rest of the init() method, like this:
if(this.before(false)){
do stuff
}else{
do something else
}
Related
I have the following existing function that I'm unable to access and change. So I've had to cache it and add other functions to it.
The function loads periodically and sometimes load on page load and sometimes doesn't. The additional functions I've added to it , I don't ever want loading on page load however.
I have tried the following with no luck. I can not prevent update_scores from loading on page load , so the if(onSwitch) is true , then and only then I want to add something else to the existing function. Once onSwith is true , then I need all additional functions to always execute when update_scores runs, but initially only after another function click() is called.
function click() {
update_scores(true);
}
update_scores = (function(onSwitch) {
var cached_function = update_scores;
return function() {
let update_scores=true; // always do something below when update_scores after function click ran once
cached_function.apply(this, arguments);
if(onSwitch) {
//Do something only when function click is ran or until onSwith is defined as true
}
};
}());
ok , i was able to achieve by setting a global variable , then changing it when i ran another function
var onSwitch = false;
function click() {
update_scores(true);
onSwitch = true;
}
update_scores = (function(onSwitch) {
var cached_function = update_scores;
return function() {
cached_function.apply(this, arguments);
if(onSwitch) {
//Do something only when function click is ran or until onSwith is defined as true
}
};
}());
I am trying to execute functions on click, Below is click button on HTML,
Insights.init() will execute on page load will give me some data from server, now with click on button, i need to pass variable to month function to filter data, and with click i want to execute all functions inside Insights()
var Insights = function() {
var initCheckColor = function(vari) {
console.log(vari);
}
var testFunction = function(vari) {
console.log('test');
}
return {
init: function() {
initCheckColor();
testFunction();
}
};
}();
jQuery(document).ready(function() {
Insights.init();
});
function month(vari) {
console.log("hoo");
return {
init: function() {
initCheckColor(vari);
testFunction();
}
};
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Month
Now problem is, i can see "hoo" printed on console when i click on link, but i also want to print it with execution of initCheckColor(vari) function, means i want output two times, but i could not output it,
How can i get output two times?
Problem: Is with this code
function month(vari) {
console.log("hoo");
//this block of code
return {
init: function() {
initCheckColor(vari);
testFunction();
}
};
// upto here
}
When you call the month function you are returning a object with a property named init Note: you are just returning a object and not executing the functions within the property. Also other issue is this property is a function which executes two other function, But those functions are not available in the current scope. As they are equal to Private methods for the Insights object.
Solution: Would be to re initialize the object with data just like how you are doing on page load.
I have fixed your code and added comments in the code where the changes were made.
var Insights = function() {
var initCheckColor = function(vari) {
console.log(vari);
}
var testFunction = function(vari) {
console.log('test');
}
return {
init: function(vari) { // have a input parameter during initialization.
initCheckColor(vari);
testFunction();
}
};
}();
jQuery(document).ready(function() {
Insights.init('something'); // I pass in the string "something" now this will be printed by the initCheckColor function.
});
function month(vari) {
console.log("hoo");
Insights.init(vari); // initialize the Insights object by passing in some value.
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Month
I have the following code
if (testNavigation() && $scope.selections.somethingChanged) {
return false;
}
in the testNavigation I am calling modal dialog and if I answer Ok in that dialog, I re-set somethingChanged to false. My problem is that when the code is executed, the testNavigation and modal dialog is by-passed and executed later and therefore my test is not working as I need it to work. What should I change in order for my logic to properly work, e.g. first invoke my modal dialog, and proceed accordingly in the main function?
This is my testNavigation method:
var testNavigation = function()
{
if ($scope.selections.somethingChanged) {
var modal = $modal.open({
controller: function ($scope) {
$scope.okClass = 'btn-primary',
$scope.okLabel = resourceFactory.getResource('Labels', 'yes'),
$scope.cancelLabel = resourceFactory.getResource('Labels', 'cancel'),
$scope.title = resourceFactory.getResource('Labels', 'unsavedChanges'),
$scope.message = resourceFactory.getResource('Messages', 'unsavedChanges');
},
templateUrl: '/app/templates/modal'
});
modal.result.then(function () {
$scope.selections.somethingChanged = false;
});
}
return true;
}
I'll try to add more details. I have LoadView() and New() functions in the Index page controller. In both of these functions I need to do the following:
if $scope.selections.somethingChanged = false I need to proceed with the logic.
if $scope.selections.somethingChanged = true I need to pop up a modal dialog asking if I want to go ahead and Cancel my changes or Stay on the current page. If I answer Yes, I want to go ahead with the logic.
So, that's the purpose of the separate testNavigation function. In the languages where each function call is sequential, that would work as I intended. But it doesn't work this way in AngularJS / JavaScript and I am not sure how to make it to work the way I need. We tried few ideas with $q service but didn't get the result.
Make testNavigation() always return a promise (either with the result of the modal, or with false straight away, when somethingChanged is false and you don't want to ask the question. Proceed when this promise is resolved:
var testNavigation = function() {
if ($scope.selections.somethingChanged) {
var modal = [...]
return modal.result; // returns a promise that will be resolved when the modal is closed
} else {
return $q.when(true); // returns a promise that will be resolved straight away with true
}
}
// when used:
testNavigation().then(function() {
...do stuff...
})
Without knowing what your test looks like, this is kind of difficult, but from what it looks like, you're creating a race condition.
You call testNavigation() which always returns true,
but because $scope.selections.somethingChanged is set to false at some point in the future,
$scope.selections.somethingChanged may not finish before the end of that evaluation- so while you're setting $scope.selections.somethingChanged to false in testNavigation, it may or may not be false when the second if is performed:
if( testNavigation() && // returns true no matter what.
$scope.selections.somethingChanged // this is changed with a promise within testNavigation. that promise could either complete or not complete before this is evaluated with the if.
){
return false;
}
var testNavigation = function() {
if ($scope.selections.somethingChanged) {
var modal = $modal.open({
// details
});
modal.result.then(function() {
// this is async and could take place any time between {locationA} and never.
$scope.selections.somethingChanged = false;
});
//{locationA}
}
return true;
}
I can imagine that producing some weird results in tests.
I updated this Question, beacuse my Fiddles were not saved well -
I'm building an app that contain a serie of similar operations with a form.
Instead of adding a lot of forms I chose to use a generic form and dynamically change the events on the buttons of the form.
My challenge is to make the code more generic, so I easily can add much more operations without multiplying the code. The complete non-generic code is in the Fiddle link, a part of that is placed here, under the link.
refined Fiddle
// sample
function init() {
operationA.addEventListener('mousedown', prepareA);
operationB.addEventListener('mousedown', prepareB);
}
// Operation flow:
// Prepare >> Cancel or Process >> Execute
function prepareA() {
if (isEventsRemoved) {
showForm();
ok.addEventListener('mousedown', processA);
cancel.addEventListener('mousedown', cancelA);
isEventsRemoved = false;
} else {
alert("Cancel other operation first");
}
}
function cancelA() {
hideForm();
cleanUp(processA, cancelA);
}
function processA() {
hideForm();
cleanUp(processA, cancelA);
if (input.value == "a") {
executeA();
} else {
alert("No good, try operationA again.");
cleanUp(processA, cancelA);
prepareA();
}
}
function executeA() {
alert("Executing A");
}
Instead of repeating the same functions/methods for A, B, C etc..
I started with generic functions, passing parameters for A, B ,C etc.
But when I follow this line, I don't know how to glue it together, some kind of recursiveness starts to appears, see the generic Fiddle. Again only part of the code under the Fiddle link.
Problem Fiddle
// Set it up
function init() {
operationA.addEventListener('mousedown', function(){ prepareAll(processA, cancelA)});
operationB.addEventListener('mousedown', function(){ prepareAll(processB, cancelB)});
}
// Operation flow:
// Prepare >> Cancel or Process >> Execute
function prepareAll(processHandler, cancelHandler) {
if (isEventsRemoved) {
showForm();
ok.addEventListener('mousedown', processHandler);
cancel.addEventListener('mousedown', cancelHandler);
isEventsRemoved = false;
} else {
alert("Cancel current operation first");
}
}
function cancelAll(processHandler, cancelHandler) {
hideForm();
cleanUp(processHandler, cancelHandler);
}
My question : Is this way really a dead end and should I for example use object instances instead of using generic functions with parameters, or am I just not following the way in the right manner ?
I have some code here that basically fades in and out some text from an array.
If I call it once, it works as intended. IE, it displays some text for 3 seconds and the fades in the next text from array.
If however I try to restart the animation again, it seems to run twice as fast. Can anyone help me? I think I can cancelling the previous animation correctly
In the code below, I call rotator.start(); just to demo the problem I am having. Just call it once to see how it should behave.
http://jsfiddle.net/zmTAC/3/
<div id="foobar"></div>
<script>
var rotator = {
quoteIndex: -1,
duration: 500,
delay: 3000,
play: false,
quotes: [],
theElement: null,
start: function (quotes, theElement) {
this.quoteIndex = -1;
this.quotes = quotes;
this.theElement = theElement;
this.stop();
this.play = true;
this.showNextQuote();
return this;
},
showNextQuote: function () {
this.quoteIndex = (this.quoteIndex + 1) % this.quotes.length;
if (this.play) {
$(this.theElement).html(this.quotes[this.quoteIndex])
.fadeIn(this.duration)
.delay(this.delay)
.fadeOut(this.duration, this.showNextQuote.bind(this));
}
return this;
},
stop: function () {
this.play = false;
$(this.theElement).stop(true, true).show();
return this;
}
};
rotator.start(["foo1", "bar1"], "#foobar");
rotator.start(["foo2", "bar2"], "#foobar");
rotator.start(["foo3", "bar3"], "#foobar");
rotator.start(["foo4", "bar4"], "#foobar");
rotator.start(["foo5", "bar5"], "#foobar");
rotator.start(["foo6", "bar6"], "#foobar");
rotator.start(["foo7", "bar7"], "#foobar");
rotator.start(["foo8", "bar9"], "#foobar");
rotator.start(["foo9", "bar9"], "#foobar");
rotator.start(["foo0", "bar0"], "#foobar");
</script>
It looks like $.stop() is not cancelling the completed callback to $.fadeOut(). I added a third element to the quotes array in the final call and every other quote was shorter (i.e. the second, then the first(in the second loop), then the third, etc.)
I'm not certain that is the cause (and if it is it is a bug in $.stop()) but a hack (but it works) is to create a container element which you compeltely remove in rotator.stop() and (re)create in rotator.start(). Like this:
var rotator = {
quoteIndex: -1,
duration: 500,
delay: 3000,
play: false,
quotes: [],
theElement: null,
start: function (quotes, theElement) {
this.quoteIndex = -1;
this.quotes = quotes;
this.stop();
this.theElement = theElement;
$(this.theElement).html('<span class="rotator"/>');
this.play = true;
this.showNextQuote();
return this;
},
showNextQuote: function () {
if (this.play) {
this.quoteIndex = (this.quoteIndex + 1) % this.quotes.length;
$('.rotator', this.theElement).html(this.quotes[this.quoteIndex])
.fadeIn(this.duration)
.delay(this.delay)
.fadeOut(this.duration, this.showNextQuote.bind(this));
}
return this;
},
stop: function () {
this.play = false;
$('.rotator', this.theElement).stop(true, true).remove();
$(this.theElement).show();
return this;
}
};
See it working in this fiddle
By calling start method you are overriding the quotes. It is playing between foo0 and bar0. What you want to do is pass a single array of quotes like so:
rotator.start(["foo1", "bar1", "foo2", "bar2", "foo3", "bar3"], "#foobar");
Works fine on Firefox and IE for me. JSFiddle I also changed to jQuery 1.11.0 as 1.10 didn't work at all on IE.
EDIT
I am not sure what went wrong on jQuery side or on browser side. What I know you were doing a known mistake with the this.play variable. You set it to false, then to true, and then checked if it was true in the same function. If you run this function twice at the same time what you might get is:
this.stop(); // second execution is on this line
this.play = true; // first execution is on this line
this.showNextQuote();
What might happen is the second execution sets this.play to false and then the first execution sets it to true. What happens next in showNextQuote method is run by both executions as this.play is true for both (showNextQuote function checks if this.play is true).
I am not sure if it was the problem and I am pretty sure it was not the problem but what I showed you is what happens when you try to run multiple asynchronous operations. I am pretty sure it is buggy jQuery. Maybe newer version fixed this?
Maybe some other community member has more to say on this?
Solution
EDIT2
Thinking of it, JS would not allow it to happen as it would not execute this code in a separate threads. I wont delete it though as it explains you how easy it is to make this mistake with asynchronous jQuery that might be causing your problem.
can you try this instead of the commented line in your stop method
//$(this.theElement).stop(true, true).show();
$(this.theElement).finish().show();
working example here