I have a hash called options. The problem that I'm facing is that options['beforeOpen'] might already be a function, in which case I don't want to overwrite it. I'd like to instead call it then call another function that needs to be called every time
In this example the method that needs to be called every time is methodThatINeedToDo. I thought the code below would accomplish this but it's not working as I expected.
function methodThatINeedToDo(){alert('maintenance');}
var options = {beforeOpen: function(){alert('first');}}
if(typeof options['beforeOpen'] == "function"){
options['beforeOpen'] = function(){options['beforeOpen'].call(); methodThatINeedToAddToDo();}
} else {
options['beforeOpen'] = methodThatINeedToDo;
}
The problem is that within the function you're defining to override options['beforeOpen'], you're using options['beforeOpen'], which by that time has been overwritten!
You need to cache it and use the cached value within your new function:
var cachedBeforeOpen = options.beforeOpen;
if (typeof cachedBeforeOpen == "function") {
options.beforeOpen = function() {
cachedBeforeOpen.call();
methodThatINeedToDo();
};
} else {
options.beforeOpen = methodThatINeedToDo;
}
Simply always call methodThatINeedToDo, since you want to and in there check to see if you should call your options method:
function methodThatINeedToDo(){
options.beforeOpen && options.beforeOpen();
alert('maintenance');
}
That really smells like the wrong solution. Why not Publish/Subscribe pattern?
Here's a little example: http://jsfiddle.net/ajyQH/
$(function() {
var yourObj = { yourFct : [] };
$('#btn').click(function() {
yourObj.yourFct.push(function() {
$('#testibert').append($('<p>').text('hallo'));
});
});
$('#btn_exec').click(function() {
var len = yourObj.yourFct.length;
for(var i = 0; i < len; i++) {
yourObj.yourFct[i]();
}
});
});
var oldCall = options['beforeOpen'];
var newCall = function(){
oldCall();
methodThatINeedToAddToDo();
};
options['beforeOpen'] = newCall;
Related
I've created a 'class' in javascript called QuoteProductService(), see below.
I've added two functions to the prototype and now, I'm trying to call one of the functions (getQuoteProductFromArray) from within a jquery $.each inside the other function (getFakeQuoteProducts). This doesn't work. I've tried adding 'this.', but this also does not work, because 'this' inside the .each refers to the current element in the loop.
How should I do this ?
function QuoteProductService() {
}
QuoteProductService.prototype.getQuoteProductFromArray = function(quoteproductarray, quoteproductid){
var founditem=null;
// do stuff
return founditem;
}
QuoteProductService.prototype.getFakeQuoteProducts = function(){
// do something to fill the mappedQuoteProducts array
$.each(mappedQuoteProducts, function (index, quoteproduct) {
if (quoteproduct!=-null) {
if (quoteproduct.parentid != "") {
// this is where it goes wrong :
var parent = getQuoteProductFromArray(mappedQuoteProducts, quoteproduct.parentid);
if (parent != null) {
parent.attachChild(quoteproduct);
}
}
}
});
}
Save a reference to your QuoteProductService instance before calling each
QuoteProductService.prototype.getFakeQuoteProducts = function(){
var _this = this;
// do something to fill the mappedQuoteProducts array
$.each(mappedQuoteProducts, function (index, quoteproduct) {
if (quoteproduct!=-null) {
if (quoteproduct.parentid != "") {
// this is where it goes wrong :
var parent = _this.getQuoteProductFromFlatArray(mappedQuoteProducts, quoteproduct.parentid);
if (parent != null) {
parent.attachChild(quoteproduct);
}
}
}
});
}
Add var self = this; to the beginning of the getFakeQuoteProducts function. Then call getQuoteProductFromFlatArray like this: self.getQuoteProductFromFlatArray.
First of all you provided wrong method name - getQuoteProductFromFlatArray instead of getQuoteProductFromArray. Secondly in JS you must provide scope for instance methods.
Easiest way to achieve this is to store this reference into some other, private variable. See the example below.
function QuoteProductService() {
}
QuoteProductService.prototype.getQuoteProductFromArray = function(quoteproductarray, quoteproductid){
var founditem=null;
// do stuff
return founditem;
}
QuoteProductService.prototype.getFakeQuoteProducts = function(){
var me = this; // store this into me
// do something to fill the mappedQuoteProducts array
$.each(mappedQuoteProducts, function (index, quoteproduct) {
// this === me will return false
if (quoteproduct!=-null) {
if (quoteproduct.parentid != "") {
// this is where it goes wrong :
var parent = me.getQuoteProductFromArray(mappedQuoteProducts, quoteproduct.parentid);
if (parent != null) {
parent.attachChild(quoteproduct);
}
}
}
});
}
JQuery can handle events implementing Observer Design Pattern, but it can handle only one event for one callback function
I was wondering, if it could handle many events: when all this events was triggered, or when all boolean premises became true then call a function
It would be much better to develop dynamic applications
Do you know if already exist something like that?
If not: do you think it would be nice if i develop?
EDIT:
i would like to do something like this:
when((a==b && c!=0), function(){
//do something when a==b and c!=0
alert("a==b && c!=0");
});
EDIT 2:
I've found a great API that allow listen for variable changes. You just have to do something like this:
obj.watch(function(){
alert("obj changes");
});
http://watch.k6.com.br/
Sounds like Promises to me. Most of the libraries developed with that concept (there are many for JavaScript, including jQuery.deferred) can simply build a new Promise for the event that some others got fulfilled.
You can easily do this manually:
var flag1 = false;
var flag2 = false;
var flag3 = false;
var checkAllFlags = function () {
if (!flag1 || !flag2 || !flag3) return;
checkAllFlags = function () { };
allFlagsSet();
}
var allFlagsSet = function () {
// Execute here when all flags are set
}
// When you set a flag do it like this:
flag2 = true;
checkAllFlags();
Or you could use this class:
window.flagger = function (count, callback, once) {
var curr = this;
curr.callback = callback;
if (once)
curr.called = false;
curr.flags = [];
curr.flagsLeft = count;
for (var i = 0; i < count; i++)
curr.flags[i] = false;
this.flag = function (index) {
if (!curr.flags[index]) {
curr.flags[index] = true;
if (--curr.flagsLeft <= 0 && (!once || !curr.called)) {
if (once) curr.called = true;
curr.callback();
}
}
};
this.unflag = function (index) {
if (curr.flags[index]) {
curr.flags[index] = false;
curr.flagsLeft++;
}
};
this.reset = function (force) {
for (var i = 0; i < count; i++)
curr.flags[i] = false;
curr.flagsLeft = count;
if (once && force)
curr.called = false;
};
this.isFlagged = function (index) {
return curr.flags[index];
};
}
And use it like this:
var myFlagger = new flagger(
/* The amount of flags that need to be set: */8,
/* The function that needs to be called when the flags are all set: */
function () { alert('Callback function called'); },
/* If the callback function should be called again when a flag is unflagged and reflagged.
Optional. Default: false */false);
// You can now use these functions:
myFlagger.flag(0); // Use this to set a flag, index ranges from 0 to count - 1.
myFlagger.unflag(0); // Unflags an index.
myFlagger.reset(); // Resets all flags to false.
myFlagger.reset(true); // Using this forces the reset, returning to full start
// state, causes callback function to be called again
// even if 'once' is specified.
alert('0 is flagged: ' + myFlagger.isFlagged(1)); // Returns if flag is flagged.
I hope this somewhat helps you.
I have the following JavaScript function which receives coordinates and returns the nearest tube station:
function coord() {
var metro = new YMaps.Metro.Closest(new YMaps.GeoPoint(<?=getCoords($addr) ?>), { results : 1 } )
YMaps.Events.observe(metro, metro.Events.Load, function (metro) {
if (metro.length()) {
metro.setStyle("default#greenSmallPoint");
var firstStation = metro.get(0);
var tubest = (firstStation.text).split("метро ");
var tube = tubest[1];
if($("span#tubest").text() == '') {
$('.whiteover').hide();
}
} else {
if($("span#tubest").text() == '') {
$('.whiteover').hide();
}
}
});
}
The value which I need to output as a result of this function execution is the value of the "tube" variable (var tube = tubest[1];). Basically a simple document.write will work. Or a simple return value like:
var tubestation = coord();
However I'm not sure how to achieve this.
You can't have this function return the value, since you're using an observer pattern - which sets up an asynchronous logic to the code. Simply saying, at the time that your coord() function returns, the value is not there yet.
To deal with this, normally you would pass a callback function, then resume your computation there.
Declare your function as:
function coord(callback)
then, after you know the value you want, call the callback with the value:
callback.call(null, tube);
Do it after your if { ... } else { ... } so your callback gets called both on success and on failure (on failure it will pass undefined, you might want to correct it by declaring var tube = null before the if).
then, instead of:
tubestation = coord();
call it like this:
coord(function(tubestation) {
// continuation of your code here
});
You probably won't be able to use document.write since the time to use it would be long past, but you can set the value as the contents of an element that you already generated. You have jQuery in your tags, so it's quite easy:
coord(function(tubestation) {
$('#tube_station').text(tubestation);
});
assuming you have <div id="tube_station"/> somewhere in your HTML.
How about this simple add to that function?
function coord() {
var metro = new YMaps.Metro.Closest(new YMaps.GeoPoint(<?=getCoords($addr) ?>), { results : 1 } )
YMaps.Events.observe(metro, metro.Events.Load, function (metro) {
if (metro.length()) {
metro.setStyle("default#greenSmallPoint");
var firstStation = metro.get(0);
var tubest = (firstStation.text).split("метро ");
var tube = tubest[1];
$('div#myDivResult').html(tube)
if($("span#tubest").text() == '') {
$('.whiteover').hide();
}
} else {
if($("span#tubest").text() == '') {
$('.whiteover').hide();
}
}
});
}
How to write this JavaScript code without eval?
var typeOfString = eval("typeof " + that.modules[modName].varName);
if (typeOfString !== "undefined") {
doSomething();
}
The point is that the name of the var that I want to check for is in a string.
Maybe it is simple but I don't know how.
Edit: Thank you for the very interesting answers so far. I will follow your suggestions and integrate this into my code and do some testing and report. Could take a while.
Edit2: I had another look at the could and maybe itis better I show you a bigger picture. I am greatful for the experts to explain so beautiful, it is better with more code:
MYNAMESPACE.Loader = ( function() {
function C() {
this.modules = {};
this.required = {};
this.waitCount = 0;
this.appendUrl = '';
this.docHead = document.getElementsByTagName('head')[0];
}
function insert() {
var that = this;
//insert all script tags to the head now!
//loop over all modules:
for (var modName in this.required) {
if(this.required.hasOwnProperty(modName)){
if (this.required[modName] === 'required') {
this.required[modName] = 'loading';
this.waitCount = this.waitCount + 1;
this.insertModule(modName);
}
}
}
//now poll until everything is loaded or
//until timout
this.intervalId = 0;
var checkFunction = function() {
if (that.waitCount === 0) {
clearInterval(that.intervalId);
that.onSuccess();
return;
}
for (var modName in that.required) {
if(that.required.hasOwnProperty(modName)){
if (that.required[modName] === 'loading') {
var typeOfString = eval("typeof " + that.modules[modName].varName);
if (typeOfString !== "undefined") {
//module is loaded!
that.required[modName] = 'ok';
that.waitCount = that.waitCount - 1;
if (that.waitCount === 0) {
clearInterval(that.intervalId);
that.onSuccess();
return;
}
}
}
}
}
};
//execute the function twice a second to check if all is loaded:
this.intervalId = setInterval(checkFunction, 500);
//further execution will be in checkFunction,
//so nothing left to do here
}
C.prototype.insert = insert;
//there are more functions here...
return C;
}());
var myLoader = new MYNAMESPACE.Loader();
//some more lines here...
myLoader.insert();
Edit3:
I am planning to put this in the global namespace in variable MYNAMESPACE.loadCheck, for simplicity, so the result would be, combining from the different answers and comments:
if (MYNAMESPACE.loadCheck.modules[modName].varName in window) {
doSomething();
}
Of course I will have to update the Loader class where ever "varName" is mentioned.
in JS every variable is a property, if you have no idea whose property it is, it's a window property, so I suppose, in your case, this could work:
var typeOFString = typeof window[that.modules[modName].varName]
if (typeOFString !== "undefined") {
doSomething();
}
Since you are only testing for the existence of the item, you can use in rather than typeof.
So for global variables as per ZJR's answer, you can look for them on the window object:
if (that.modules[modName].varName in window) {
...
}
If you need to look for local variables there's no way to do that without eval. But this would be a sign of a serious misdesign further up the line.
In trying to make my Javascript unobtrusive, I'm using onLoads to add functionality to <input>s and such. With Dojo, this looks something like:
var coolInput = dojo.byId('cool_input');
if(coolInput) {
dojo.addOnLoad(function() {
coolInput.onkeyup = function() { ... };
});
}
Or, approximately equivalently:
dojo.addOnLoad(function() {
dojo.forEach(dojo.query('#cool_input'), function(elt) {
elt.onkeyup = function() { ... };
});
});
Has anyone written an implementation of Ruby's andand so that I could do the following?
dojo.addOnLoad(function() {
// the input's onkeyup is set iff the input exists
dojo.byId('cool_input').andand().onkeyup = function() { ... };
});
or
dojo.byId('cool_input').andand(function(elt) {
// this function gets called with elt = the input iff it exists
dojo.addOnLoad(function() {
elt.onkeyup = function() { ... };
});
});
I don't know Dojo, but shouldn't your first example read
dojo.addOnLoad(function() {
var coolInput = dojo.byId('cool_input');
if(coolInput)
coolInput.onkeyup = function() { ... };
});
Otherwise, you might end up trying to access the element before the DOM has been built.
Back to your question: In JavaScript, I'd implement andand() as
function andand(obj, func, args) {
return obj && func.apply(obj, args || []);
}
Your example could then be written as
dojo.addOnLoad(function() {
andand(dojo.byId('cool_input'), function() {
this.onkeyup = function() { ... };
});
});
which isn't really that much shorter than using the explicit if statement - so why bother?
The exact syntax you want is not possible in JavaScript. The way JavaScript executes would need to change in a pretty fundamental fashion. For example:
var name = getUserById(id).andand().name;
// ^
// |-------------------------------
// if getUserById returns null, execution MUST stop here |
// otherwise, you'll get a "null is not an object" exception
However, JavaScript doesn't work that way. It simply doesn't.
The following line performs almost exactly what you want.
var name = (var user = getUserById(id)) ? user.name : null;
But readability won't scale to larger examples. For example:
// this is what you want to see
var initial = getUserById(id).andand().name.andand()[0];
// this is the best that JavaScript can do
var initial = (var name = (var user = getUserById(id)) ? user.name : null) ? name[0] : null;
And there is the side-effect of those unnecessary variables. I use those variables to avoid the double lookup. The variables are mucking up the context, and if that's a huge deal, you can use anonymous functions:
var name = (function() {return (var user = getUserById(id)) ? user.name : null;})();
Now, the user variable is cleaned-up properly, and everybody's happy. But wow! what a lot of typing! :)
You want dojo.behavior.
dojo.behavior.add({
'#cool_input': {
onKeyUp: function(evt) { ... }
}
});
How about something like this:
function andand(elt, f) {
if (elt)
return f(elt);
return null;
}
Call like this:
andand(dojo.byId('cool_input'), function(elt) {
// this function gets called with elt = the input iff it exists
dojo.addOnLoad(function() {
elt.onkeyup = function() { ... };
});
});
As far as I know there isn't a built-in JavaScript function that has that same functionality. I think the best solution though is to query by class instead of id and use dojo.forEach(...) as you will be guaranteed a non-null element in the forEach closure.
You could always use the JavaScript equivalent:
dojo.byId('cool_input') && dojo.byId('cool_input').whateverYouWantToDo(...);
I've never used dojo, but most javascript frameworks (when dealing with the DOM) return the calling element when a method is called from the element object (poor wording, sorry). So andand() would be implicit.
dojo.addOnLoad(function() {
dojo.byId('cool_input').onkeyup(function(evt) { /*event handler code*/
});
});
For a list:
Array.prototype.andand = function(property, fn) {
if (this.filter(property).length > 0) this.map(fn);
}