I have a small problem with prototyped JS coding, and with callbacks. It looks like not working properly.
Here is my sample:
var hl = new HeaderLogin;
hl.drawPanel();
var HeaderLogin = function(elem) {
this.init = true;
this.jsvh = JSViewHandler.getInstance();
};
HeaderLogin.prototype.drawPanel = function() {
var self = this;
...
this.jsvh.get({
...
'callback': function(rsp, templates) {
...
$('#jsview_user_login_form').ajaxForm({success: asd});
}
});
function asd(rspJSON, statusText, xhr, $form) {
self.showResponse(rspJSON, statusText, xhr, $form);
}
};
HeaderLogin.prototype.showResponse = function(rspJSON, statusText, xhr, $form) {
if (typeof this.init === 'undefined') {
alert('not an object');
}
...
}
I have to call the showResponse function after the form has been sent, but if I use the {success: self.showResponse} the init will not exists. It looks like a static call and I can't access any variable from the constructor. If I create a local asd function and I use it as the success callback the showRespons will know about the constructor variables.
I don't want to use this extra function, if you have any solution about this problem, please let me know!
Thanks a lot guys! :)
SOLUTION:
success: self.showResponse.bind(self)
I have not done this in a long time, but can you try with
'callback': function(rsp, templates) {
...
var s = self;
$('#jsview_user_login_form').ajaxForm({success: s.showResponse});
}
Related
I have the following code:
someClass1 = function () {
this.doStuff = function () {
PubSub.publish('topic1', { id: 1 });
}
}
someClass2 = function () {
this.forename = 'bob2';
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, this.forename);
});
}
function log() {
console.log(arguments);
}
var c1 = new someClass1();
var c2 = new someClass2();
c1.doStuff();
and I am using the pubsubjs library (https://github.com/federico-lox/pubsub.js)
The code is simple - publish a message and handle it in another class (someClass2) using PubSub
My question is that when I publish a message and handle it in someClass2, this is undefined. This occurs at the line: log(msg, data, this.forename);
This means I cant access any of the someClass2 properties/functions. What do I need to do to get the this to not be undefined? Is this possible? Are they other libraries that will help? Am I doing it wrong....
All help apprenticed! Thanks
You're passing an unbound function to subscribe. Such a function has "no idea" about this. You have to bind it:
PubSub.subscribe("topic1", (function (msg, data) {
log(msg, data, this.forename);
}).bind(this));
this is not what you expect inside the callback, just cache a copy of this as another variable outside:
someClass2 = function () {
this.forename = 'bob2';
var that = this;
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, that.forename);
});
}
I'm trying to protect a part of my js code wrapping my code with an Unknown function.
I have edit my function to change
function banana(url) {}
to method
banana: function(url){ },
when I try to call my function banana in another function i try to use
this.banana(url);
but i have this error:
TypeError: this.banana is not a function
Full code:
(function (){
var url_test = "./add_user.php?opt=get_user&token=<?php echo $token; ?>";
if (typeof $.customfnc == 'undefined')
$.customfnc = {}
$.customfnc.get = {
setup: function (){
var url = "google.ca";
this.banana(url);
},
banana: function (url){
console.log("my url: " + url);
};
};
};
// on ready render data
$(document).ready(function() {
$.customfnc.get.setup();
});
})(jQuery);
thanks for your help!
The issue here is that the scope of 'this' is not exactly what you might think it is.
The way I have handled this particular issue in the past is to add
var self = this;
Outside of the object that is attempting to self reference. This may impact how you have set up youre .get() object though.
$.customfnc.get = function(){
var self = this;
self.setup = function (){
//....
self.banana(URL)
}
self.banana = function(url){
//...
}
}
Is what I've done below a sensible approach to allow callbacks to run on functions defined in an object's prototype, such that the scope is correct?
I've been wrestling with the correct way to set the value of this when an object's prototype method is the one to run in response to a callback which might originate from an AJAX request or from a click binding or whatever.
Here is a simplified annotated version:
// everything is inside an object which provides the namespace for the app
var namespace = {
// a fairly vanilla object creation routing, which uses the prototype
// approach for defining the functions on the object
newObj : function(params) {
var MyObj = function(params) {
this.property = params.property
};
MyObj.prototype = namespace.ObjPrototype;
return new MyObj(params);
},
// the prototype itself, which defines 2 related functions
ObjPrototype : {
// The first is called to do some form of asynchronous operation
// In this case it is an ajax call
doAsync: function (params) {
$.ajax({
type: "get",
url: params.url,
data: params.data,
dataType: params.datatype,
success: namespace.objClosure(this, "asyncSuccess", ["data"]),
});
// the final line above is the key here - it asks a function (below)
// for a closure around "this", which will in turn run the
// function "asyncSuccess" (defined next) with the argument "data"
},
// This is the actual callback that I want to run. But we can't
// pass this.asyncSuccess to the ajax function above, because the
// scope at execution time is all wrong
asyncSuccess : function(params) {
this.property = params.data;
},
},
// This is the bit I sort of invented, to help me around this problem.
// It returns a function which provides a closure around the object
// and when that returned function is run it inspects the requested
// arguments, and maps them to the values in the JS default
// "arguments" variable to build a parameters object which is then
// passed to the function on the object
objClosure : function(obj, fn, args) {
return function() {
if (args) {
var params = {};
for (var i = 0; i < args.length; i++) {
params[args[i]] = arguments[i];
}
obj[fn](params);
} else {
obj[fn]();
}
}
}
}
Now, obviously the actual target callback MyObj.asyncSuccess needs to know that it's going to get a params object, and what structure it will be, and that knowledge has to be shared by the invoking function MyObj.doAsync, but otherwise this seems to work well.
My question is - am I totally mad? Have I missed something obvious that would solve this problem for me in a simpler/less convoluted way? Am I just too far down the rabbit hole by this stage?
I've read around a lot of questions on SO and they have all addressed part of my question, but I don't seem to have got to the bottom of a generally accepted solution for this. I can't be the only person who's ever wanted to do this :)
Edit
I've accepted the answer below, but you need to read all the comments too for it to come together. Thanks folks for your help!
aren't you over complicating things? see if the below code will help you. i did not completely understand your intent but the below code should help you
function newObj(params) {
function asyncSuccess(params) {
this.property = params.data;
}
function objClosure(obj, fn, args) {
return function() {
if (args) {
var params = {};
for (var i = 0; i < args.length; i++) {
params[args[i]] = arguments[i];
}
obj[fn](params);
} else {
obj[fn]();
}
}
}
this.property = params.property
this.doAsync = function (params) {
console.log('reached async');
$.ajax({
type: "get",
url: params.url,
data: params.data,
dataType: params.datatype,
success: objClosure(this, "asyncSuccess", ["data"]),
});
}
}
var k = new newObj({'property':'xyz'});
k.doAsync();
After seeing the comment from "GameAlchemist" i looked into objClosure function i think we can further improvise by using below code: I am still not sure what the value of this.property or data is to give a proper solution hence just assuming few things
function newObj(params) {
function asyncSuccess(params) {
this.property = params ? params.data : null;
}
function objClosure(args) {
return function() {
if (args) {
var params = {};
for (var i = 0; i < args.length; i++) {
params[args[i]] = arguments[i];
}
asyncSuccess(params);
} else {
asyncSuccess();
}
}
}
this.property = params.property
this.doAsync = function (params) {
console.log('reached async');
$.ajax({
type: "get",
url: params.url,
data: params.data,
dataType: params.datatype,
success: objClosure(["data"]),
});
}
}
Few issues here:
if you are already passing params.data to data i.e. data:params.data how can you again assign the value this.property = params.data? Few things are confusing but i hope the above solution works : )
I have couple of modules that do their own thing, but need them to sometimes access a property of one another (not that intertwined, just one json obj). Like so
var Bananas = (function() {
// Bananas.properties would look like this
// Bananas.properties = { 'color' : 'yellow' };
var methodToGetProperties = function() {
API.get('bananas')
.done(function(data) {
Bananas.properties = data;
}
};
var publiclyReturnProperties = function() {
if (!Bananas.properties) {
methodToGetProperties();
} else {
return Bananas.properties;
}
};
var doSomethingBananas = function() {
bananas.doing.something;
bananaHolder.innerHTML = Bananas.properties;
}
var init = function() {
doSomethingBananas
}
return {
init: init,
properties: publiclyReturnProperties,
};
})();
var Apples = (function() {
var doSomethingApples = function() {
apple.innerHTML = Bananas.properties.color;
};
var init = function() {
doSomethingApples();
};
return {
init: init
};
})();
Bananas.init(); Apples.init();
Now, the way I do it now is by simply revealing the methodToGetProperties, which returns the API call, and then work on using jQueries deferred method wherever I call it. But I feel this ruins my code by putting .done everywhere.
I've been reading up to singleton pattern and feel it might be the solution to my problem, but I'm not sure how to implement it. Or maybe implement a callback function in methodToGetProperties, but again not confident as to how.
Would kindly appreciate advice on how to organise my app.
Why is the following not working:
(function($){
Drupal.my_module = {};
Drupal.behaviors.my_module = {
attach: function(context, settings) {
$('#some-div').Drupal.my_module.doStuff();
}
};
Drupal.my_module.doStuff = function(){
this.append('Hello World');
}
})(jQuery);
I get this error: TypeError: $(...).Drupal is undefined
If I use another architecture like passing the selector as an argument for the function it works:
(function($){
Drupal.my_module = {};
Drupal.behaviors.my_module = {
attach: function(context, settings) {
Drupal.my_module.doStuff($('#some-div'));
}
};
Drupal.my_module.doStuff = function(elem){
elem.append('Hello World');
}
})(jQuery);
It also works if I declare the function in the jQuery.fn object:
$.fn.doStuff = function(){...do something...}; // It works
Then call it like:
$('#my-div').doStuff(); // It works
But I want to put it under Drupal.my_module, something more like the first case.
Any clues?
Thanks!
$('#some-div').Drupal.my_module.doStuff();
Drupal is not a valid jQuery method or property, hence the undefined error.