I made code like this, to easier connecting callbacks on events:
dojo.ready(function() {
for(var action in page.actions) {
for(var key in page.actions[action]) {
(function() {
dojo.query(key).connect(action, function(evt) {
if(page.actions[action][key]() == false)
dojo.stopEvent(evt);
});
})();
}
}
});
page = {
actions :
{
onclick :
{
"#page-action-one" : function()
{
alert("Action 1");
return false;
},
"#page-action-two" : function()
{
alert("Action 2");
return false;
}
}
}
};
But click on "#page-action-one" an "#page-action-two" make the same alert("Action 2"). I tried to use cloer, but without effect. I now, I can make it different way, but I would like to now, why is this happening.
You're trying to fix the closure issue by wrapping your event handler assignment in an anonymous function. But the key to that trick is that you have to pass in the looping variable (or variables) as an argument to the anonymous function - otherwise the anonymous function wrapper does nothing. Try:
dojo.ready(function() {
for(var action in page.actions) {
for(var key in page.actions[action]) {
(function(action, key) {
dojo.query(key).connect(action, function(evt) {
if(page.actions[action][key]() == false)
dojo.stopEvent(evt);
});
})(action, key);
}
}
});
This "fixes" the value of action and key at the time the anonymous function is called, so within the anonymous function those variable names only apply to the passed arguments, not to the named variables in the outer scope, which will update on the next loop iteration.
Related
This weekend, I ran into a peculiar problem of handling race conditions in Javascript.
Here is the code that was giving me problem:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call
makeAjaxCall(requestObj, function(data) {
//Callback for the ajax call
//PROBLEM : HOW CAN I ACCESS VALUE OF 'i' here for the iteration for which the AJAX call was made!!!
}
}
}
Accessing the value of 'i' inside the AJAX callback would not give me the expected value because by the time AJAX call response comes back, the 'for' loop would have crossed many more iterations.
To handle this, I used the following approach:
function myFunction(requestObj, myArray) {
var i = 0;
function outerFunction() {
makeAjaxCall(requestObj, innerFunction);
}
function innerFunction(data) {
i++;
if(i<myArray.length) {
outerFunction();
}
}
outerFunction();
}
Is this the correct approach? Any other way I can improve this assuming it is a 3rd pary AJAX library call which I can't modify.
You just need to use a closure:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call closed over i
(function(i) { // wrap your call in an anonymous function
makeAjaxCall(requestObj, function(data) {
// i is what you think it is
}
})(i) // pass i to the anonymous function and invoke immediately
}
}
The issue is that the callbacks you're passing to the ajax call have an enduring reference to i, not a copy of its value when they were created.
Your approach is fine except that it waits to make the second ajax call until the first finishes, then waits for the second to finish before the third, etc. Unless you have to do that (and I get the impression you don't), it's better to let them overlap.
A couple of options:
Use a builder function:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call
makeAjaxCall(requestObj, buildHandler(i));
}
function buildHandler(index) {
return function(data) {
// Use `index` here
};
}
}
Now, the handler has a reference to index, which doesn't change, rather than i, which does.
Use Function#bind:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call
makeAjaxCall(requestObj, function(index, data) {
// Use index here
}.bind(null, i));
}
}
Function#bind creates a function that, when called, will call the original function with a specific this value (we're not using that above) and any arguments you pass bind — followed by any arguments given to the bound function.
I prefer #1: It's clear to read and doesn't create a bunch of unnecessary functions (whereas in theory, #2 creates two functions per loop rather than just one).
There are two other ways of doing this: using bind, using a closure
Using bind:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
makeAjaxCall(requestObj, function(idx,data) {
//idx will be correct value
}.bind(null,i));
}
}
Using a closure:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
(function(idx){
makeAjaxCall(requestObj, function(data) {
//idx will be correct value
});
})(i);
}
}
There is also third method, use another function to create your callback
function myFunction(requestObj, myArray) {
function makeCB(idx){
return function(){
//do stuff here
}
}
for(var i=0;i<myArray.length;i++) {
makeAjaxCall(requestObj, makeCB(i));
}
}
I have an angular animation on an ng-repeat (using jQuery's animate function), and want to update a "Visible" property on the model when each animation completes. The purpose is to orchestrate multiple animations. I am finding this works fine in the enter function, but does not work in the leave function. Here is the code:
app.animation('.step-animation',
function () {
return {
enter: function (element, done) {
var scope = angular.element(element).scope();
jQuery(element).css({ opacity: 0 });
jQuery(element).animate({
opacity: 1
}, 2000, done);
return function (cancelled) {
if (cancelled) {
jQuery(element).stop();
} else {
scope.$apply(function () {
scope.step.Visible = true;
});
}
}
},
leave: function(element, done) {
var scope = angular.element(element).scope();
jQuery(element).animate({
opacity: 0
}, 1000, done);
return function (cancelled) {
if (cancelled) {
jQuery(element).stop();
} else {
scope.$apply(function () {
scope.step.Visible = false;
});
}
}
}
}
});
The Visible property on my step object is being successfully set to true after the enter animation completes, but it is not updated on the leave animation. Also - I notice that the $parent property is null on the scope in the leave function, but populated on the scope in the enter function. It seems like leave is isolated, and enter is not. How can I update the scope from the leave function?
UPDATE
I have discovered that the return function is getting called twice in the leave function - once with cancelled = true. No idea what is causing this, or how to troubleshoot.
For each element in the iterated collection a DOM template will be rendered and a new scope will be created and associated with it.
The leave function in your code is called when one of these templates is removed from the DOM (for example due to filter). When the template is removed its associated scope is destroyed.
This means that when the following function executes the associated scope will already have been destroyed:
return function (cancelled) {
if (cancelled) {
jQuery(element).stop();
} else {
scope.$apply(function () {
scope.step.Visible = false;
});
}
};
You can verify this by adding the following inside it:
console.log(scope.$$destroyed);
When a scope is destroyed one of the things that happens it that its $apply function is set to an empty function:
function noop() {}
This means that the anonymous function (the one that sets Visible to false) that you pass to scope.$apply will never be executed, since $apply is just an empty function and doesn't do anything with its passed arguments.
To solve this you can inject $rootScope and use that instead:
return function(cancelled) {
if (cancelled) {
jQuery(element).stop();
} else {
$rootScope.$apply(function() {
scope.step.Visible = false;
});
}
};
Demo: http://plnkr.co/edit/jdNtcOSzq9BC9vxjmmLy?p=preview
Also note that the element will already be a jqLite element (or jQuery element if jQuery is loaded before AngularJS) so you can replace the following:
var scope = angular.element(element).scope();
With:
var scope = element.scope();
And all occurences of:
jQuery(element)
With just:
element
Just like in the PHP I need a variable variable.
I`m getting a data-type from the html and that variable is actually a function in the js code.
And I get it I need to call it.
Every time its different, so I don`t know which one exactly is.
if (e.which == 114 || e.which == 116) {
var action = $('.active').data("action");
action();
}
function testing() {
alert('yes');
}
Here the function name is testing.And the variable action is holding it.
How I`m suppose to call it ?!
I remember that there was a easy syntax for that,but I cant find it.
Thanks
You could extract the value and use eval().
<div id="something" data-action="alert('test')">SOME DIV</div>
$(document).ready(function() {
var $myObject = $("#something");
var action = $myObject.data('action');
$myObject.click(function() {
eval(action);
});
});
Try it yourself on jsfiddle
However, eval is evil
It depends on the object which holds the function. For the global scope use : window[action](). Otherwise, replace window with the name of your object : myObject[action](). However, this solution is not suitable for functions declared inside a private scope :
function f(fnName) {
function a() { alert('a') };
function b() { alert('b') };
fnName(); // error
}
In this case, you could use eval like this (⚠ keep control over inputs ⚠) :
function f(fnName) {
function a() { alert('a') };
function b() { alert('b') };
eval(fnName)();
}
Otherwise you could wrap them inside an object like so :
function f(fnName) {
var wrapper = {};
wrapper.a = function () { alert('a') };
wrapper.b = function () { alert('b') };
wrapper[fnName]();
}
f('a'); // alerts "a"
f('b'); // alerts "b"
I see different topics about the toggle function in jquery, but what is now really the best way to toggle between functions?
Is there maybe some way to do it so i don't have to garbage collect all my toggle scripts?
Some of the examples are:
var first=true;
function toggle() {
if(first) {
first= false;
// function 1
}
else {
first=true;
// function 2
}
}
And
var first=true;
function toggle() {
if(first) {
// function 1
}
else {
// function 2
}
first = !first;
}
And
var first=true;
function toggle() {
(first) ? function_1() : function_2();
first != first;
}
function function_1(){}
function function_2(){}
return an new function
var foo = (function(){
var condition
, body
body = function () {
if(condition){
//thing here
} else {
//other things here
}
}
return body
}())`
Best really depends on the criteria your application demands. This might not be the best way to this is certainly a cute way to do it:
function toggler(a, b) {
var current;
return function() {
current = current === a ? b : a;
current();
}
}
var myToggle = toggler(function_1, function_2);
myToggle(); // executes function_1
myToggle(); // executes function_2
myToggle(); // executes function_1
It's an old question but i'd like to contribute too..
Sometimes in large project i have allot of toggle scripts and use global variables to determine if it is toggled or not. So those variables needs to garbage collect for organizing variables, like if i maybe use the same variable name somehow or things like that
You could try something like this..: (using your first example)
function toggle() {
var self = arguments.callee;
if (self.first === true) {
self.first = false;
// function 1
}
else {
self.first = true;
// function 2
}
}
Without a global variable. I just added the property first to the function scope.
This way can be used the same property name for other toggle functions too.
Warning: arguments.callee is forbidden in 'strict mode'
Otherwise you may directly assign the first property to the function using directly the function name
function toggle() {
if (toggle.first === true) {
toggle.first = false;
// function 1
}
else {
toggle.first = true;
// function 2
}
}
Just wondering if there is anyway to fire some code when a function is called, without adding the code to the function, for example:
function doSomething(){
//Do something
}
//Code to call when doSomething is called
You can wrap the function :
(function(){
var oldFunction = doSomething;
doSomething = function(){
// do something else
oldFunction.apply(this, arguments);
}
})();
I use an IIFE here just to avoid polluting the global namespace, it's accessory.
Well, yes, it's not actually hard to do. The crucial thing is that a function's name is just an identifier like any other. You can redefine it if you want to.
var oldFn = doSomething;
doSomething = function() {
// code to run before the old function
return oldFn.apply(this, arguments);
// code to run after the old function
};
NB that it's better to do oldFn.apply(this, arguments) rather than just oldFn. In many cases it won't matter, but it's possible that the context (i.e. the value of this inside the function) and the arguments are important. Using apply means they are passed on as if oldFn had been called directly.
What about something like:
function doSomething(){
doSomething.called = true;
}
//call?
doSomething();
if(doSomething.called) {
//Code to call when doSomething is called
}
I know you said you don't want to modify the original function, but consider adding a callback. Then you can execute code based on different results in your function (such as onSucess and onError):
function doSomething(onSuccess, onError){
try {
throw "this is an error";
if(onSuccess) {
onSuccess();
}
} catch(err) {
if(onError) {
onError(err);
}
}
}
Then, when you call doSomething, you can specify what you want done with inline functions:
doSomething(function() {
console.log("doSomething() success");
}, function(err) {
console.log("doSomething() error: " + err);
});