Using the original `this` in an event triggered outside - javascript

This would be better explained in code:
var FileResourceManager = {
LoadRequiredFiles: function (config) {
config = config || {};
this.OnLoading = config.onLoadingCallback;
this.OnComplete = config.onCompleteCallback;
//this works fine here.
if (this.OnLoading) {
this.OnLoading();
}
Modernizr.load([{
load: 'somefile.js',
complete: function () {
//Error in this callback here.
if (this.OnComplete) {
this.OnComplete();
}
}
});
}
};
FileResourceManager.LoadRequiredFiles({
onLoadingCallback: function () {
alert('started');
},
onCompleteCallback: function () {
alert('complete');
}
});
As you can see, in the callback for Modernizr.load's complete event, I want to call the method of the parent/outer object. But this actually became the Modernizr object. How can I access the properties of the outer object inside an event?
I've seen this done in the backbone.js project, by using some form of binding. I'm not sure if I need to write something like this.

var self = this;
Modernizr.load([{
load: 'somefile.js',
complete: function () {
//Error in this callback here.
if (self.OnComplete) {
self.OnComplete();
}
}
});

Modernizr.load([{
load: 'somefile.js',
complete: (function () {
//Error in this callback here.
if (this.OnComplete) {
this.OnComplete();
}).bind(this)
}
});

The this object redefined in the scope of the Modernizr function. It is customary to define a variable called that outside the scope and use it to refer to the outer this.
Alternatively, you could just use the config object, like this:
complete: function () {
if (config.OnComplete) {
config.OnComplete();
}
}

Related

Calling a JavaScript function from jQuery returns ReferenceError: method not defined

var MyObj = core.Class.extend({
connect: function() {
$('body').keydown(function (e) {
// want to call disconnect() in here
});
},
disconnect: function() {
}
});
I've tried calling disconnect(), this.disconnect(), but they all return,
ReferenceError: disconnect is not defined
The variable core here is:
core = require('some_module')
You need to store the this context into a variable.
Your code is using this context of this function: $('body').keydown(function (e) {...}
var MyObj = core.Class.extend({
connect: function() {
var $self = this;
$('body').keydown(function(e) {
$self.disconnect();
});
},
disconnect: function() {}
});
The this is not the right this.
Use this :
$('body').keydown((e)=>{
// this.disconnect() in here
})
instead
Lambda functuons keep the context on which they run.
You can also use bind function and also keep a variable which keeps the context and then refer to that variable

Calling a function in another function with AngularJS factory

I have an AngularJS factory that has multiple functions.
I want to call one of the functions inside the other function as shown below:
.factory("AppStart", function($cordovaSQLite) {
return {
init: function() {
var res = "hello";
console.log("in load start up page");
},
create_table: function() {
AppStart.init();
}
}
});
But I get the following error:
AppStart is not defined.
So how do I call the init() function in the create_table() function? I have tried just calling init(), but it doesn't work either.
To accomplish this, I recommend defining your functions with names, and then creating a service object with properties that refer to them, as I did below:
.factory("AppStart", function($cordovaSQLite) {
function init() {
var res = "hello";
console.log("in load start up page");
}
function create_table() {
init();
}
return {
init: init,
create_table: create_table
};
});

Pass this to callback

I know what the problem is but not sure what's the best option to solve this issue. I have got a callback and I'm not able to access this from it. I don't want to have any variable outside the scope to refer this. Can I pass this as a parameter?
var myModule = Module.create({
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications, headers);
},
refresh: function() {
},
_wsNotifications: function ( message ) {
this.refresh(); //Error: 'this' is undefined because it's a callback
}
});
One way you can solve this is using function.bind at the source When you specify the callback do
ws.subscribe('/topic/notifications', this._wsNotifications.bind(this), headers);
or cache the this to a variable.
var myModule = Module.create({
self : this,
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications, headers);
},
refresh: function() {
},
_wsNotifications: function ( message ) {
self.refresh(); //Error: 'this' is undefined because it's a callback
}
});
Give this a try.
var myModule = Module.create({
var self = this;
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications, headers);
},
refresh: function() {
},
_wsNotifications: function ( message ) {
self.refresh(); //Error: 'this' is undefined because it's a callback
}
});
return interactions;
});
note the creation and use of the self variable instead of the this variable. Using this method will preserve this, even when it would normally change scope.
You can make use of ECMAscript's bind function Function.prototype.bind.
init: function() {
ws.subscribe('/topic/notifications', this._wsNotifications.bind(this), headers);
},
Now, this within _wsNotifications will refer to the object you bound it to.

Variable Scope: this.remove is not a function

this.remove() is not a function. How come?
var vehicle = function () {
return {
init: function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
this.remove();
});
},
remove: function () {
alert('test');
}
}
}();
jQuery().ready(vehicle.init);
Sorry for the confusion. I'm trying to call my own "remove" function. This is simply a class to manage vehicles on my page. This is the beginning of it and it will have a lot more functions than just init/remove.
this is a DOM element. To use jQuery's .remove() method, you need to wrap it in a jQuery object.
$(this).remove();
EDIT: If you were hoping to call the remove() function in the vehicle object, then call:
vehicle.remove();
Also, if you were hoping to shorten your .ready() call, you can do this:
jQuery(vehicle.init);
From the jQuery 1.4 release notes:
The jQuery().ready() technique still works in 1.4 but it has been deprecated. Please use either jQuery(document).ready() or jQuery(function(){}).
Maybe you're looking for something like this?
var vehicle = new function () {
var self = this;
this.init = function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
self.remove();
});
};
this.remove = function () {
alert('test');
};
};
...or like this maybe? It's kind of hard to tell what you're going for...
var vehicle = new function () {
function remove () {
alert('test');
}
this.init = function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
remove.call(this);
});
};
};
Note - we're all somewhat confused because it's not clear which "remove" function you want to call.
The problem is that you're passing in the reference to the "init" function, but when it's called the "this" variable will refer to the window object, not the value of "vehicle". Why? Because in Javascript the "this" value depends only on how a function is called. The fact that two functions are defined in the same object has absolutely nothing to do with it.
Try doing this instead:
jQuery(function() {
vehicle.init();
});
When you call the "init" function that way — by explicitly referencing it as a property of the "vehicle" object — then Javascript will bind "this" to the value of "vehicle".
edit oh wait I just noticed that you're also going to have to revise your "init" function, because that code inside the "click" handler is going to be called by jQuery in such a way as to bind "this" in that context to the affected element. Thus if you want to keep the "vehicle" reference around, you'd do this:
init: function () {
var originalThis = this;
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
originalThis.remove();
});
},
Since you said you're trying to call your own remove function, here's how to do it:
var vehicle = (function () {
return {
init: function () {
var that = this; // step one
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
that.remove();
});
},
remove: function () {
alert('test');
}
}
}()); // step zero - wrap the immediate invocation in parens
jQuery(function () {
vehicle.init(); // step two
);
var vehicle = function () {
return {
init: function () {
var self = this;
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
self.remove();
});
},
remove: function () {
alert('test');
}
}
}();
jQuery().ready(function() {
vehicle.init();
});
When invoking a function as a method "this" refers to the object that is invoking it. In jQuery the function passed is invoked as a method of the html element so "this" becomes the element.
To make sure you are refering to the correct object you'll need to create a reference to the original object.
var vehicle = function () {
var that = {
init: function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
that.remove();
});
},
remove: function () {
alert('test');
}
}
return that;
}();
jQuery().ready(vehicle.init);

How do you add objects to a javascript namespace?

var Test = (function() {
return {
useSub: function () {
this.Sub.sayHi();
},
init: function () {
$(document).ready(this.useSub);
}
};
})();
Test.Sub = (function () {
return {
sayHi: function () {
alert('hi');
}
};
})();
Test.useSub(); // works
Test.init(); // explodes
Above I am trying to create a Test namespace and add an object Sub to it. I was doing fine until I tried using the object in jQuery. The error is "Uncaught TypeError: Cannot call method 'sayHi' of undefined". If there is a better way to do this, I am open to it.
Edit:
Obviously this was demo code. In my real application the solution that I went with because I think it is the most clear is this one:
var Namespace (function () {
return {
init: function () {
$(document).ready(function() {
Namespace.onReady();
}
},
onReady: function() {
alert('Now I am back in the Namespace scope. Proceed as planned');
}
};
})();
Edit2: All jQuery callbacks seem to require they are used in this manner or else the scoping is screwed up.
I think it is a scope problem. If you do
$(document).ready(this.useSub);
then this.useSub will be executed in the window scope (so inside the function, this refers to the window object) and there doesn't exist a Sub attribute.
Try:
init: function () {
var obj = this;
$(function(){obj.useSub()});
}
For some reason it does not work using $(document).ready(function(){obj.useSub()}); but it works with the $() shortcut.
Here is one way
var Test = {
useSub : function () {
Test.Sub.sayHi();
},
init: function () {
$(document).ready(Test.useSub);
},
Sub: {
sayHi: function () {
alert('hi');
}
}
};
in this line:
$(document).ready(this.useSub);
you're passing a reference to a function and the scope is lost- when the function runs, this no longer means Test.

Categories