Javascript: Defining a map of functions - javascript

$("#searchType").on('change', function () {
var selectionAction = {
All: loadAll(),
Competitions: loadAll("competitions"),
Clubs: loadAll("clubs"),
Teams: loadAll("teams")
};
var selection = $("#searchType").find('option:selected').val();
selectionAction[selection]
});
See the above code. The idea is that when selection equals one of the properties in my object, then the corresponding function will be called.
e.g. when selection equals Competitions then we would invoke loadAll("competitions") function.
Instead what I am finding is that when it enters the onChange function that it invokes all functions.
What am I doing wrong here?

Use anonymous functions to make the call. Currently you are storing the result of the function call which is undefined
var selectionAction = {
All: function(){loadAll()},
Competitions: function(){loadAll("competitions")},
Clubs: function(){loadAll("clubs")},
Teams: function(){loadAll("teams")}
};
var selection = $("#searchType").find('option:selected').val();
selectionAction[selection]();// make sure to call the anonymous function
Or, if you prefer brevity,
$("#searchType").on('change', function () {
loadAll($("#searchType").find('option:selected').val().replace("All","").toLowerCase())
});

When you specify loadAll(), loadAll("competitions"), loadAll("clubs") and so on you are actually executing the function immediately. What you want to do is have your object have properties of non-function calls like so:
var selectionAction = {
All: '',
Competitions: 'competitions',
Clubs: 'clubs',
Teams: 'teams'
};
And then do:
var selection = $("#searchType").find('option:selected').val();
loadAll(selectionAction[selection]);
And make sure your loadAll function checks for existence of its 1st argument.

Related

javascript array value as variable name , how I get them from function

I'm using array value as variable and then call the function N method, how I get them in function N.
I really want to simulate the Javascript array method, I don't want to use parameters to achieve it. For example,
var p1 = [1,2,3,4,5]; p1.push(6);
function _Array() {
this._this = this;
}
_Array.prototype.show = function () {
this._this.forEach(function(item){alert(item);}) //how to print 1,2,3,4,5
};
var p1 = [1,2,3,4,5];
p1 = new _Array();
//p1._Array.call(p1); //not work
// new _Array().show.call(p1); //not work
// p1.show(); //not work
You have to store that in the instance
function N(arr) {
this._this = arr
}
N.prototype.say = function () {
this._this.forEach(function (item) {
console.log(item)
})
}
p1 = new N([1, 2, 3, 4, 5])
p1.say()
If you are insistent on wanting to write a method that takes the array by reference, you can modify the array prototype like so:
Array.prototype.show = function() {
this.forEach(item => alert(item));
}
However, it is a VERY BAD IDEA to modify the built in object prototypes, as this can cause conflicts with external libraries implementing their own "show" function that is being used differently, or cause incompatibilities with future versions of JS that implements this method.
It would be far more prudent in most situations to pass the array as a parameter, unless you have a very specific reason why you're not doing so. In that case, you should at least prefix the method with some sort of project identifier to minimize the chances of conflicts occurring.

Pass Current Object Key

Is there any way to pass the current key of an object as a parameter to a method executed as its value? For example:
VersionOne: {
welcomeMessage: this.localization.get(this.currentKey(?))
},
VersionTwo: {
welcomeMessage: this.localization.get(this.currentKey(?))
}
I know that I can just write out the keys manually, but for long keys, I don't want to duplicate them.
You can't do it before the object has been defined, but you can keep your code DRY by assigning it later:
const versions = {};
['VersionOne', 'VersionTwo'].forEach((version) => {
versions[version] = {
welcomeMessage: () => console.log(version),
};
});
versions.VersionTwo.welcomeMessage();

unable to get value that is set in a callback

I'm having an issue getting a value that is set in a callback. I initially make a call to get Quest data, then call game.state.setNPCs after the quest data has returned.
I want to get the NPC object after it has been set, but the get is returning an empty array even though setNPCs() seems to be setting the array.
You can see below, after the callback to set the result.npcs, I log out getNPCs(), and it is an Empty Array.
Even more weird, I call game.state.getNPCs() from within the GameState object after the value has been set, but it is still an empty array.
EDIT: I find if I pass in game.state.getNPCs as a callback into the initial callback setNPCs(), like so:
callback(result.npcs, game.state.getNPCs);
Then this works... But I don't want to have to pass in another callback. See below.
Initial call with game.state.setNPCs callback:
Utilities.game.quest.getQuestData({ id : stat.quest_id }, game.state.setNPCs);
Call to getQuestData:
getQuestData : function (params, setNPCcallback) {
API.Quest.getQuestData(params).done(function (result) {
if (game.state) {
game.state.setQuest(result); //Object received successfully
setNPCcallback(result.npcs, game.state.getNPCs);
console.log('NPCs', game.state.getNPCs()); //Empty array
}
});
},
GameState object:
var GameState = function(args) {
this.npcs = [];
...
};
GameState.prototype = {
constructor : GameState,
getNPCs : function () {
console.log(this.npcs); //Empty array
return this.npcs;
},
setNPCs : function (npcsArray, getNPCcallback) {
this.npcs = npcsArray;
console.log(this.npcs); //Contains Object
console.log(game.state.getNPCs()); //Empty array
console.log(getNPCcallback()); //Contains Object
},
I made a small demo to test the issue.
var Person = function () {
this.name = "someone";
}
Person.prototype.walk = function () {
console.log(this)
};
var p = new Person;
function exec(callback) {
callback(); //context is Window
callback.call(p); //context is Person {name: "someone"}
}
p.walk(); //context is Person {name: "someone"}
exec(p.walk);
When you invoke the callback from getQuestData, the context is not GameState instance. Invoking the callback with the correct context using call or apply methods, or using a callback which is bound to correct context using bind method should fix the issue.
Else you can pass the GameState instance itself and invoke gameState.callbackMethod()
which should look like the following according to previous example
function exec(instance) {
instance.walk(); //context is Person {name: "someone"}
}
exec(p);

Setting "this" to the instance, in a callback set during the creation of a prototype function

I have this code:
var createAllAreSelectedClickedHandler = function(selectablesArrayGetter) {
return function() {
var array = selectablesArrayGetter();
var desiredState = array.every(function(selectable) { return selectable.selected; }) ? false : true;
array.forEach(function(selectable) {
selectable.selected = desiredState;
});
};
};
Followed by this one:
function PromoViewModel() { this.registrations = [...] }
PromoViewModel.prototype.allEventsSelectedClickedHandler = createAllAreSelectedClickedHandler(function() { return this.registrations; }));
I can't manage to set the correct value of this. The "this" value when the function is created points to Window so I can't do .bind(this). I've tried doing .bind(PromoViewModel.prototype) but it lacks all the precious instance fields set inside the constructor.
I know I could simply set this.allEventsSelectedClickedHandler in the constructor function, but I'm trying to separate the methods creation from the variables.
The problem is the call selectablesArrayGetter(); which determines the this value for the callback.
You will need to "pass" the this value that the method (i.e. the closure you are returning) is invoked on, using call:
var array = selectablesArrayGetter.call(this);
I'd recommend defining your PromoViewModel.prototype.allEventsSelectedClickedHandler method as follows:
PromoViewModel.prototype.allEventsSelectedClickedHandler = function() {
var _array = this.registrations;
var desiredState = _array.every(function(selectable) { return selectable.selected; }) ? false : true;
_array.forEach(function(selectable) {
selectable.selected = desiredState;
});
};
the function that you're passing as callback uses this, but doesn't have the PromoViewModel context. You can ensure the method has the proper context by binding this to a variable.
function PromoViewModel()
{
var me = this;
this.registrations = [...];
this.allEventsSelectedClickedHandler = createAllAreSelectedClickedHandler(function() {
return me.registrations;
});
}
Working fiddle: http://jsfiddle.net/michaschwab/coegnL5j/9/ also has Bergi's answer in there (commented out) to show that that works just as well.
Ok here is what I did.
In the prototype definition instead of directly associating it to createAllAreSelectedClickedHandler function, I actually define a function that returns the createAllAreSelectedClickedHandler function. By doing this, I can define a variable (in this case protoScope) that maps this context when defined.
When doing that, if you put a break-point in the createAllAreSelectedClickedHandler function you will see that the selectablesArrayGetter value is correct (the acutal registrations array).
PromoViewModel.prototype.allEventsSelectedClickedHandler = function (){
var protoScope = this;
return createAllAreSelectedClickedHandler(function() {
return protoScope.registrations;
});
}

OO JavaScript circumventing .call

Take a look at the fiddle here
In the show function the JavaScript call method is used to make this refer to the container variable in my contactForm object. I think, I'm not too sure about the magic that makes this work. Can someone elucidate why this does work, and what a good alternative might be?
JS
$(document).ready(function () {
var contactForm = {
container: $('#contact'),
config: {
effect: 'slideToggle',
speed: 400
},
/*******************/
init: function (config) {
$.extend(this.config, config);
$('<button>').text('Contact me')
.attr('type', 'button')
.insertAfter('#firstArticle')
.on('click', this.show);
//currently only logic on the close button
},
/*******************/
show: function () {
//using variable names to shorten up
var cf = contactForm,
container = cf.container,
config = cf.config;
if (container.is(':hidden')) {
cf.close.call(container);
container[config.effect](config.speed);
}
},
/*******************/
close: function () {
var self = $(this);
if (self.find('span.close').length) {
return;
}
$('<span>').addClass('close')
.text('close')
.prependTo(this)
.on('click', function () {
//self= span
self[contactForm.config.effect](500)
});
}
};
contactForm.init();
});
There's no magic at all; that's just how call works. call lets you call a JavaScript function and manually specify the this value therein, followed by all of the parameters, listed out individually.
So
cf.close.call(container);
calls cf.close with the this value set to container. Hypothetically, this
cf.close.call(container, 1, 'b');
would do the same thing, except also pass in 1 and 'b' as parameters.
Call is very, very similar to apply, with the difference being that apply takes all parameters as an array, rather than being listed out individually. So the (hypothetical) second example would be the same as
cf.close.apply(container, [1, 'b']);
This can be incredibly useful when you want to call another function, set the this value, and wholesale pass all of the current function's arguments along for the ride. Ie
someFunction.apply(thisValue, arguments);

Categories