javascript namespace call other function methods - javascript

I try to change some way to call methods into namespace.
Calling parent methods (I dont think its possible)
Creating and call inheritance function
Calling inside another method (mostly jquery onReady event function) (this.MyFunction() not working)
I split every namespace in files (want to keep it that way)
I try How to call function A from function B within the same namespace? but I didn't succed to split namespaces.
my fiddle sample got only 1 sub-namespace but could be more.
https://jsfiddle.net/forX/kv1w2rvc/
/**************************************************************************
// FILE Master.js
***************************************************************************/
if (!Master) var Master = {};
Master.Print= function(text){
console.log("master.Print :" + text);
$("body").append("<div>master.Print : " + text + "</div>");
}
/**************************************************************************
// FILE Master.Test1.js
***************************************************************************/
if (!Master) var Master = {};
if (!Master.Test1) Master.Test1 = {};
/**************************************************************************
* Descrition :
* Function for managing event load/documentReady
**************************************************************************/
Master.Test1.onReady = function () {
$(function () {
Master.Test1.Function1(); //try to replace because need all namespace.
try {
this.Function2(); //not working
}
catch(err) {
console.log("this.Function2 not working");
$("body").append("<div>this.Function2 not working</div>");
}
try {
this.Print("onReady"); //not working
}
catch(err) {
console.log("this.Print not working");
$("body").append("<div>this.Print not working</div>");
}
try {
Print("onReady"); //not working
}
catch(err) {
console.log("Print not working");
$("body").append("<div>Print not working</div>");
}
});
}
Master.Test1.Function1 = function () {
console.log("Function1");
$("body").append("<div>Function1</div>");
this.Function3(); //working because not inside another function
}
Master.Test1.Function2 = function () {
$("body").append("<div>Function2</div>");
console.log("Function2");
}
Master.Test1.Function3 = function () {
$("body").append("<div>Function3</div>");
console.log("Function3");
Master.Print("Function3"); //try to replace because need all namespace.
}
Master.Test1.onReady();
I use Master.Test1.Function1(); and I want to change that because Function1 is inside the same namespace.
I use Master.Print("Function3"); I dont think I can change that. the way I try to use it, it's more an inheritance function. but I dont know if theres a way to do that?
Maybe I should change the my namespace methode? maybe prototype will do what I want?

You can capture the this in a variable because this inside $(function() {}) will point to document object. The below will work provided you never change the calling context of onReady -- i.e. it is always called on the Test1 object and not called on other context:
Master.Test1.onReady = function () {
var self = this;
$(function () {
self.Function1();
// ..
});
}
To access Print you have to reference using the Master object like: Master.Print() as it won't be available in the Test1 object

this is document within .ready() or jQuery() alias for .ready() where function(){} is parameter $(function() {}). this at this.Function2() will reference document.

"Objects" in javascript are not built the same way as in most object-oriented languages. Essentially, what you are building is a hierarchy of static methods that have no real internal state in-and-of themselves. Therefore, when one of the defined methods is invoked, the context (or state) of that method depends on what object invoked the method.
If you want to have any internal context, you will need to create an "instance" of an "object prototype". At that point, you can use "this.otherFunction" within your other functions. Here is a small example:
var MyObject = function() {};
MyObject.functionOne = function() {
console.log("Function 1");
this.functionTwo();
};
MyObject.functionTwo = function() {
console.log("Function 2");
};
var instanceOne = new MyObject();
instanceOne.functionOne();
You might get some more information about object definition here

Related

Call function within another in Javascript

I've the following script:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
});
});
And within the following code I would like to re-run the update(); function.
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
datumbtn.className = 'jaar';
dimensions1 = 'ga:month,ga:nthMonth';
start1 = moment(now).date(1).month(0).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(1, 'year').date(1).month(0).format('YYYY-MM-DD');
end2 = moment(now).date(1).month(0).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'M';
format2 = 'MMM';
update();
}
else {
datumbtn.className = 'maand';
dimensions1 = 'ga:date,ga:nthWeek';
start1 = moment(now).subtract(2, 'day').date(1).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'month').format('YYYY-MM-DD');
end2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'YYYYMMDD';
format2 = 'Do';
update();
}
}
But somehow this doesn't work. I also tried in the above script:
window.update = function (data) {}. But that also doesn't work.
How can I call the update(); function that is situated inside the gapi.analytics.ready(function() {} ?
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready().
It's really a simple matter of moving the function declaration
function update (data) {
// same as existing code
}
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update );
});
And passing in data needed when you call it in your other function
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
..........
update(datumbtn);
}.......
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready()
That's not actually true - you can have it global and there at the same time. Whether you want to, is a different manner, as that would pollute the global namespace and so on. However, here is how that can be achieved:
First, extract the update function outside of the ready handler like so
function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
}
This will create a new function with the name update which accepts one parameter called data. Thanks to hoisting it would not matter if it's before or after anywhere you want to use it, as it would be effectively "pulled" to the top.
Next, you can just use the function inside the ready handler like so:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update);
});
Since .on(events, handler) accepts a function as the second parameter, you can just provide a function reference there. It doesn't matter that your function is technically declared elsewhere, as it is still going to be called with the same arguments. Similarly, if you replace update with alert you will be giving the reference to window.alert so you will get an alert with data.
With that, you can just call the same function in your other piece of code.
That is true for any place that uses callbacks, including setTimeout - you can just give a function reference and it's going to be called. Internally, those kinds of functions almost always do something like callback() or callback(someData), occasionally callback.call(/* parameters */) where callback is the passed in argument. Whether you define that argument as you are calling the function, e.g., selector.on("click", function() {/* code */}) or separately, e.g.,
function clickHandler() { /* code */ }
selector.on("click", clickHandler)
matters little.
With that said, whether you want the function global is a different matter. Unless both pieces of code are in the same place, a global function may be the easiest way. You could, also, namespace anything your app uses, which would partially avoid the global pollution. Not completely, but sometimes you just need to have things living under window if you have multiple files, in which case, you can define your own little corner there to play with: window.myApp = window.myApp || {} would create a new object that can serve as namespace and so you will be able to do things like myApp.update = function(data) { /* code */ } and thus share that code.
If your two pieces of code are indeed in one file, then you merely need to create the function outside both using var update = function(data) { /* code */ } then hand it to each in the exact same way, since update is still going to be a function reference, however, if assigned to a variable, it won't be added to the global namespace (nor would the declaration be hoisted).

JavaScript: TypeError: xyz is not a function when calling the function

I am trying to come up with a page on which, when user clicks a file button on the page, I try to execute the JS on the page. And I am trying to use OOP / class so hopefully it can be reused later. Here is my test code:
// This is the "class".
function BearUpload() {
// some values will go here...
}
// Add a few functions
BearUpload.prototype.function1 = function () {
console.log("function1 called");
}
BearUpload.prototype.handleFileSelect = function (evt) {
console.log("handleFileSelect called");
this.function1();
}
var myBear = new BearUpload(); // Create a global variable for the test
$(document).ready(function () {
var some_condition_goes_here = true;
if (some_condition_goes_here) {
$("#my-file-select-button").change(myBear.handleFileSelect);
}
});
However, it gets error like:
TypeError: this.function1 is not a function
this.function1();
Any idea about this?
Thanks!
Bind myBear to your change eventListener
In general when you access this from handleFileSelect, this refers to the html element.
i.e. this = <input type="file" id="my-file-select-button">
$("#my-file-select-button").change(myBear.handleFileSelect.bind(myBear));
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
MDN doc
You are trying to call function1 on DOM object but you have to call on jQuery object
$(this).function1();
That's because when bound as a handler to jQuery events, this would refer to the element on which the event is triggered.
I would rather change your code like this
// Create only one global variable for your app
var APP = {};
// Create class using immediate function/closure
APP.BearUpload = (function(){
//declare private variables here
// Constructor
var bearUpload = function() {
// some values will go here...
}
// Add a few functions
bearUpload.prototype.function1 = function () {
console.log("function1 called");
}
bearUpload.prototype.handleFileSelect = function (evt) {
console.log("handleFileSelect called");
this.function1();
}
return bearUpload;
}());
APP.myBear = new APP.BearUpload();
$(document).ready(function () {
var some_condition_goes_here = true;
if (some_condition_goes_here) {
$("#my-file-select-button").change(function(e){
// do something with event 'e'
APP.myBear.handleFileSelect.call(APP.myBear, e);
});
}
});
do not use "this", it is confusing some time.
BearUpload.prototype ={
function1:function(){
var self = this;
...
},
handleFileSelect:function(e){
var self = this;
...
}
}

Spying on jQuery $('...') selector in jasmine

When it comes to spying on jQuery functions (e.g. bind, click, etc) it is easy:
spyOn($.fn, "bind");
The problem is when you want to spy on $('...') and return defined array of elements.
Things tried after reading other related answers on SO:
spyOn($.fn, "init").andReturn(elements); // works, but breaks stuff that uses jQuery selectors in afterEach(), etc
spyOn($.fn, "merge").andReturn(elements); // merge function doesn't seem to exist in jQuery 1.9.1
spyOn($.fn, "val").andReturn(elements); // function never gets called
So how do I do this? Or if the only way is to spy on init function how do I "remove" spy from function when I'm done so afterEach() routing doesn't break.
jQuery version is 1.9.1.
WORKAROUND:
The only way I could make it work so far (ugly):
realDollar = $;
try {
$ = jasmine.createSpy("dollar").andReturn(elements);
// test code and asserts go here
} finally {
$ = realDollar;
}
Normally, a spy exists for the lifetime of the spec. However, there's nothing special about destroying a spy. You just restore the original function reference and that's that.
Here's a handy little helper function (with a test case) that will clean up your workaround and make it more usable. Call the unspy method in your afterEach to restore the original reference.
function spyOn(obj, methodName) {
var original = obj[methodName];
var spy = jasmine.getEnv().spyOn(obj, methodName);
spy.unspy = function () {
if (original) {
obj[methodName] = original;
original = null;
}
};
return spy;
}
describe("unspy", function () {
it("removes the spy", function () {
var mockDiv = document.createElement("div");
var mockResult = $(mockDiv);
spyOn(window, "$").and.returnValue(mockResult);
expect($(document.body).get(0)).toBe(mockDiv);
$.unspy();
expect(jasmine.isSpy($)).toEqual(false);
expect($(document.body).get(0)).toBe(document.body);
});
});
As an alternative to the above (and for anyone else reading this), you could change the way you're approaching the problem. Instead of spying on the $ function, try extracting the original call to $ to its own method and spying on that instead.
// Original
myObj.doStuff = function () {
$("#someElement").css("color", "red");
};
// Becomes...
myObj.doStuff = function () {
this.getElements().css("color", "red");
};
myObj.getElements = function () {
return $("#someElement");
};
// Test case
it("does stuff", function () {
spyOn(myObj, "getElements").and.returnValue($(/* mock elements */));
// ...
});
By spying on the window itself you have access to any window properties.
As Jquery is one of these you can easily mock it as below and return the value you require.
spyOn(window, '$').and.returnValue(mockElement);
Or add a callFake with the input if it needs to be dynamic.

JavaScript: How to bind a method?

JSFiddle: http://jsfiddle.net/M2ALY/3/
My goal is to make a module that I can use and distribute. Therefore I must not pollute the global namespace. The module I'm making is also going to be used multiple times on one web page. That's why I chose to use OOP, but this introduced a problem.
I want my object to bind a function to be run when the user clicks an element in the DOM. In this simplified example I made, I want an alert box to pop up when the user clicks a paragraph. As an example, one of the things I need in the real project I'm working on is: The user clicks a canvas, the function figures out where the user clicked and saves it to this.clientX and this.clientY.
Instead of doing
this.bind = function() {
$("p1").bind('click', function() {
// code here
});
}
I figured it would work if I did:
this.bind = function() {obj.codeMovedToThisMethod()}
The problem is that this isn't a good design. Inside the "class" you shouldn't need to know the name of the object(s) that is going to be made of this "class". This doesn't get better when I'm making multiple objects of the "class"...
So I figured I could do
$("p1").bind('click', function(this) {
// code here
});
}
But it didn't work because sending this into the function didn't work as I thought.
How should I solve this problem?
Here is a simplified sample problem. (Same as JSFiddle.)
var test = function() {
this.alert = function() {
alert("Hi");
}
this.bind = function() {
$("#p1").bind('click', function() {
obj.alert();
});
}
}
window.obj = new test();
obj.bind();
// What if I want to do this:
var test2 = function() {
// Private vars
this.variable = "This secret is hidden.";
this.alert = function() {
alert(this.variable);
}
this.bind = function() {
$("#p2").bind('click', function(this) {
obj2.alert();
this.alert();
});
}
}
window.obj2 = new test2();
obj2.bind();​
Thanks!
Read MDN's introduction to the this keyword. As it's a keyword, you can't use it as a parameter name.
Use either
this.bind = function() {
var that = this;
$("#p2").on('click', function(e) {
that.alert();
// "this" is the DOM element (event target)
});
}
or $.proxy, the jQuery cross-browser equivalent to the bind() function:
this.bind = function() {
$("#p2").on('click', $.proxy(function(e) {
this.alert();
}, this));
}

Javascript Class Inheritance

Can anyone tell me why my 'showDiv_boo' is undefined inside the class´s method?
I also can´t access my class´s methods.
Here´s my class 'Blink' class with its properties and methods:
function Blink(div) {
this.div = div
}
Blink.prototype.counter = 0
Blink.prototype.showDiv_boo = true
Blink.prototype.showDiv = function() {
this.div.style.visibility = 'visible'
}
Blink.prototype.hideDiv = function() {
this.div.style.visibility = 'hidden'
}
Blink.prototype.startEngine = function() {
if (this.showDiv_boo) {
this.showDiv()
} else if (!this.showDiv_boo) {
this.hideDiv()
}
this.showDiv_boo = !this.showDiv_boo
this.counter++
}
Blink.prototype.startEffect = function() {
this.idEffect = setInterval(this.startEngine, 1000 / 45)
}
So, if I create:
_blink = new Blink(myDiv);
_blink.startEffect();
You can test... the variable 'showDiv_boo', is undefined inside the method.
Even, if I set the showDiv_boo inside the method to true, it won´t call my class´s methods showDiv or hideDiv.
Anyone?
Thanks :)
The reason why is that startEngine is called from setInterval. The way in which this callback is invoked causes startEngine to have a different value for this than startEffect. You need to save this in order to maintain it in the callback. For example.
Blink.prototype.startEffect = function () {
var self = this;
self.idEffect = setInterval(function () { self.startEngine(); }, 1000 / 45);
};
You need to:
use var self and call the method via self.startEngine()
use an anonymous function to wrap the call in [1] i.e. function(){ self.startEngine(); }
This is because when you just pass this.startEngine or self.startEngine you are just passing the function startEngine without specifying what this is, which in both cases is supplied by the global conext of DOMWindow.
To give an example...
function startEngine() {
...code omitted...
};
Blink.prototype.startEngine = startEngine;
Blink.prototype.start = function() {
setTimeout(startEngine, 0); // obviously wrong, what is this?
setTimeout(Blink.startEngine, 0); // actually the same as line above, although not as obvious
setTimeout(startEngine.bind(this), 0); // works correctly
}
works to add code to the prototype and if used in the anonymous function will work as expected, but if you just use Blink.startEngine as the callback it is exactly the same as using startEngine only the second is more obviously wrong because there's no object it is being called on so you'd expect this to be whatever is supplied by the context.
The other way you could do this without using the anonymous function would be
Blink.startEngine.bind(self)
Which returns a function that will call startEngine with the correct this same as explicitly creating the anonymous function and wrapping the call to self.startEngine()
Heres a link to a fiddle to play around with the differences: http://jsfiddle.net/bonza_labs/MdeTF/
If you do the following, you will find it is defined
var x = new Blink('hello');
x.showDiv_boo
Javascript uses prototypical inheritance. While showDiv_boo may not be explicitly defined within the instance of Blink that you now have, it does exist within the prototype that Blink inherits from. When you try referencing showDiv_boo from within the object, the Javascript engine realizes the object does not own a member by that name and then will check its prototype.
Along with setting a temporal variable to store this, you must call the startEngine() function with that variable:
Blink.prototype.startEffect = function(){
var self = this;
self.idEffect = setInterval(function(){ self.startEngine.call(self); }, 1000/45);
}
Note the .call(self), which basically calls the function with the variable self, so the variable this in startEngine will be the correct one.

Categories