How to properly control the context when using d3.json event handler - javascript

I am trying to implement an asynchronous request with d3.js inside self-executing anonymous function.
Rather than saying d3.json(url, callback), I use d3.json(url) to first create the request object, then register a "load" event listener with xhr.on before starting the request with xhr.get.
Here is an example, in my object I have a render function that does render charts. I would like to call this render function once my data are loaded from an API.
The call is made properly but inside my render function i am not able to call my parseData function. It seems that I have lost the this context.
The console throw this exception : Uncaught TypeError: this.parseData is not a function
Does I make something wrong ? Thanks
(function() {
var Ploter = {
render: function(jsondata) {
this.parseData(jsondata); // where the error is raised
// draw the line chart
// ....
},
parseData: function(data) {
console.log(data);
},
init: function() {
this.bindEvents();
},
bindEvents: function() {
var self = this;
d3.json(this.DATAURL)
.on("load", this.render)
.on("error", function(error) { console.log("failure!", error); })
.get();
}
Ploter.init();
})();

Instead of passing the function directly, you can wrap it in another function, creating a closure. The inner function will be able to reference the value you stored in self, which refers to the original Ploter you're looking for:
bindEvents: function() {
var self = this;
d3.json(this.DATAURL)
.on("load", function(data) { self.render(data); })

Related

How to get parent function This inside child function using async.each

var self= this; //parent function context/this
async.each(smpleArray, sampleFunc, function(err){
// if any of the saves produced an error, err would equal that error
});
This is sample function :
var sampleFunc= function(){
var self = this; //window object
//do something
}
I want to fetch the parent's this context inside child.
But i am getting windows object there.
I tried:
async.each(smpleArray, sampleFunc, function(err){
// if any of the saves produced an error, err would equal that error
}.bind(this));
But its not working.
How to get the parent's self/this inside child function ?
You have to bind the context to the correct function, i.e. sampleFunc like this:
sampleFunc.bind(this)
So your example would be:
var sampleFunc = function () {
// this is set to parent function context/this
// do something
};
async.each(sampleArray, sampleFunc.bind(this), function (err) {
// if any of the saves produced an error, err would equal that error
});

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.

js execute function after object is defined

I need for a function to be executable only after an object is defined, I'm currently working in a fascade pattern and one method is dependent on another method. in this case 'addNewLayer' fails because 'setFullMap' hasn't finished executing. is there a solution? I'm using jquery and vanilla js so most any solution would be helpful at this point:
var jen = (function(){
function setFullMap(mapID){
jen.map = new Map(mapID);
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id);
addNewLayer(opt);
}
};
})();
Thanks
solution:
var jen = (function(){
function setFullMap(mapID, callback) {
jen.map = new Map(mapID);
if(jen.map){
callback();
}
}
return {
samp: function(id, opt){
setFullMap(id, function(){
addNewLayer(opt);
}.bind(this));
}
};
})();
You will have to pass a callback function to setFullMap, and execute it once the function has completed (at the very end, before the closing }).
var jen = (function(){
function setFullMap(mapID, callback){
jen.map = new Map(mapID);
callback();
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id, function() {
addNewLayer(opt);
}.bind(this));
}
};
})();
Do not forget using .bind(this) - it is very important in order to keep the original this in your callback function.
Edit:
Actually that would not work work if the Map constructor is a-synchronous. If you do not have access to the constructor and/or you cannot pass it a callback, then presumably the only (and sad) option would be to use a setTimeout or (easier) setInterval, continuously checking at defined intervals if the operation has been completed, and then fire the callback.
You could use a callback parameter:
function setFullmap(mapId,callback) {
jen.map = new Map(mapId);
callback();
}
....
samp: function(id, opt){
setFullMap(id,function() {
addNewLayer(opt);
});
}
When u dont have a way to manipulate the Map Object then u need to use a loop:
var loop=self.setInterval(function(){
if(jen.map) {
//execute code here after jen.map is defined
console.log(typeof jen.map);
window.clearInterval(loop);
}
},50);
Check jsfiddle:
http://jsfiddle.net/9yv5t/1/
I have checked the docs and it seems that there are various events you could listen to.
For example:
var m = new Map(...);
m.on('load', function () {
//execute code when the first layer is ready
});
var l = new Layer(...);
l.on('load', function () {
//execute code when the layer has been initialized
});
It's also carefully stated for the Layer.load event:
fires after layer properties for the layer are successfully populated.
This event must be successful before the layer can be added to the
map.

Javascript this object and bind

new Something({
events:{
load: (function loopsiloop(){
console.log(this); // firstime this will be Something, but next call what its
setTimeout(function(){
console.log(this);
$.ajax({
url: 'foo.htm',
context: this,
success: function( response ){
// do something with the response
},
error: function(){
// do some error handling. you
// should probably adjust the timeout
// here.
},
complete: function(){
loopsiloop(); // recurse //need to bind this here like loopsiloop().bind(this)
}
});
}.bind(this), 5000);
}),
click: function(){
alert("clicked");
}
}
})
Please go through the code and read comments, here the problem is i need to use this in setTimeOut function, so I am binding this to setTimeOut, but when I am calling function as recursive the value of this will not be same
NB:- I dont want to pass the object to the function and dont want to use setIntervel (http://www.erichynds.com/javascript/a-recursive-settimeout-pattern/)
Your recursive call can be written like this:
complete: function() {
loopsiloop.call(this);
}
to ensure that the context is correctly set the second time around.
It could also be written like this, but it's not recommended as it'll call .bind over and over on each pass:
complete: loopsiloop().bind(this) // NB: no function wrap, just passes the ref
Don't bind and don't use this. Set var someVariable = this; before you call setTimeout and let it remain in scope for the recursion (using it instead of this inside the function).

How do you call other internal methods from inside a javascript closure with backbone.js?

Here is my example object to demonstrate the issue.
Dog = Backbone.Model.extend({
initialize: function () {
},
Speak: function (sayThis) {
console.log(sayThis);
},
CallInternalSpeak: function () {
this.Speak("arf! from internal function.");
},
CallSpeakFromClosure: function () {
this.Speak("arf! fron outside closure.");
var callClosure = function () { // think of this closure like calling jquery .ajax and trying to call .Speak in your success: closure
console.log("we get inside here fine");
this.Speak("say hi fron inside closure."); // THIS DOES NOT WORK
}
callClosure();
}
});
var rover = new Dog;
rover.Speak("arf! from externally called function");
rover.CallInternalSpeak();
rover.CallSpeakFromClosure();
Since you are in Backbone, you can always use Underscore's bind function as well. After you define callClosure, you can wrap it with a proper binding:
callClosure = _.bind(callClosure, this);
The old "self" trick... make a reference to this, call it self, and reference it in the function.
CallSpeakFromClosure: function () {
this.Speak("arf! fron outside closure.");
var self = this;
var callClosure = function () {
console.log("we get inside here fine");
self.Speak("say hi fron inside closure."); // THIS DOES NOT WORK
}
callClosure();
}

Categories