How to call a member function from another member function in Javascript - javascript

Say I have some code like this
function Chart(start, end, controller, method, chart)
{
console.log('Chart constructor called');
this.start = start;
this.end = end;
this.controller = controller;
this.method = method;
this.chart = chart;
this.options = {};
}
Chart.prototype.update = function()
{
console.log('update ' + new Date().getTime());
$.getJSON('index.php', {
controller: this.controller,
method: this.method,
START: this.start,
END: this.end },
function(json) { this.draw(json); }); //<-- Problem right here!
}
Chart.prototype.draw = function(json)
{
//lots of code here
}
I'm getting the error Uncaught TypeError: Object #<an Object> has no method 'draw'. Now, I'm the first to admit that I'm pretty new to Javascript. Am I supposed to call member functions in another way? Or am I supposed to do something different altogether?
edit: Here is how I'm creating my object:
chartObj = new Chart(start, end, 'OBF.RootCauses', 'ajaxRootCauses', chart);

The problem here is that this is changed because you are defining a new function - so this refers to the function you are in.
There are other ways how to get around this, but the simplest way would be to save this to a variable and call the function on that variable, something like this:
Chart.prototype.update = function()
{
console.log('update ' + new Date().getTime());
var self = this;
$.getJSON('index.php', {
controller: this.controller,
method: this.method,
START: this.start,
END: this.end },
function(json) { self.draw(json); });
}
See Chris's answer for a different approach for solving the same problem.

Since you're already using jQuery, you can change this line
function( json ) { this.draw( json ); });
to this:
$.proxy( this.draw, this ) );
That will preserve the context where the function was called (i.e., the this variable).

Related

How to get a UI variable out of a function

I'm sorry if this is basic. I'm self taught and stuggle with what are probably simple features of Javascript programming. Can anyone show me what I should be searching for to find the right kind of answer, OR help me in general with this one?
I have a function inside a UI window to quickly populate it with panels. But I can't seem to get the values entered into each panel get OUT of the UI and into the main "DoSomething" function.
The variable (dlg.tryThis) won't update? Why? What do I need to do?
// making a simple UI
function UI_Builder(doSomething)
{
var dlg = new Window( "dialog", "options" );
dlg.mainGroup = dlg.add( "group", undefined,"main" );
//Created a simple function so I can re-create panels and values very easy
function makePanel (panelName, val) {
var newPanel = eval("dlg.mainGroup." + panelName);
newPanel = dlg.mainGroup.add( 'panel', undefined, panelName );
newPanel.Start = newPanel.add("edittext", [0,0,30,20], val);
dlg.Start = newPanel.Start.text
newPanel.Start.onChanging = function()
{
dlg.Start = newPanel.Start.text
alert("Changed " + panelName + " to: " + dlg.Start) // THIS alerts correctly. It know what value I changed and where I changed it.
return dlg.Start;
}
return dlg.Start;
}
// calling the function to create panels.
dlg.tryThis = makePanel("tryThis", 4);
dlg.anotherOne = makePanel("anotherOne", 3);
dlg.again = makePanel("again", 2);
dlg.mainGroup.btnCancel = dlg.mainGroup.add( "button",[0,0,130,70], "doSomething" );
dlg.mainGroup.btnCancel.onClick = function()
{
doSomething();
}
return dlg
}
// all the UI builder is doing is showing the UI
var dlg = UI_Builder(doSomething);
dlg.show();
function doSomething()
{
dlg.close();
alert(dlg.tryThis) // this is not the NEW value? How do I get it to be the NEW value updated by the ".onChanging" function?
}
try passing dlg to the event handler instead of declaring it globally:
dlg.mainGroup.btnCancel.onClick = function() {
doSomething(dlg);
}
function doSomething(dlg) {
dlg.close();
alert(dlg.tryThis);
}
If you take this approach you should not declare var dlb globally, to avoid confusion. (Shadowing variables is generally a bad practice)
You're calling doSomething without any context.
Use func.apply:
dlg.mainGroup.btnCancel.onClick = function()
{
doSomething.apply(this, []);
}
And handle the object as 'this':
function doSomething()
{
this.close();
alert(this.tryThis) // this is not the NEW value? How do I get it to be the NEW value updated by the ".onChanging" function?
}

JavaScript call object method by its name [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
javascript object, access variable property name?
I'm trying to call a method of an object by its name when certain event happens - here's my code:
var imageObject = {
sendImage : function(obj) {
"use strict";
var thisId = obj.attr('id');
var thisSubmit = obj.attr('data-submit');
var thisCallback = obj.attr('data-callback');
var thisUrl = $('#' + obj.attr('data-url')).text();
new AjaxUpload(thisId, {
action: thisUrl,
name: 'thisfile',
onSubmit: imageObject.thisSubmit,
onComplete: imageObject.thisCallback
});
}
}
I would also like to pass the parameters to the submit and callback method.
At the moment nothing happens and I'm sure I'm not calling it right - could anyone explain how to achieve this?
I've also used the following for submit and complete :
onSubmit: function(file, extension) {
imageObject.thisSubmit(file, extension);
},
onComplete: function(file, response) {
imageObject.thisCallback(file, response);
}
But what I get is the error saying :
imageObject.thisSubmit is not a function
The attributes only store the name (string) of the method within the object - so it would be for instance 'uploadImage'.
TJ's answer is nearly there, except that (per the new code in your question) you need to either copy the arguments from onSubmit to thisSubmit, or use .apply() to copy them for you.
The code below uses the latter method, which avoids having to duplicate the function signature over and over:
new AjaxUpload(thisId, {
action: thisUrl,
name: 'thisfile',
onSubmit: function() {
imageObject[thisSubmit].apply(imageObject, arguments);
},
onComplete: function() {
imageObject[thisCallback].apply(imageObject, arguments);
}
});
If I've understood your question correctly, you need to use the square bracket syntax to access the properties:
new AjaxUpload(thisId, {
action: thisUrl,
name: 'thisfile',
onSubmit: function () { //Anonymous function so you can pass arguments
imageObject[thisSubmit]("myArg"); //Square brackets here
},
onComplete: imageObject[thisCallback] //Square brackets here (no arguments)
});
I'm assuming the thisSubmit and thisCallback local variables are meant to be the names of the functions that exist on imageObject.
Do both call those methods using names from those strings, and pass in arguments, you use a combination of bracketed syntax and closures:
var imageObject = {
sendImage : function(obj) {
"use strict";
var thisId = obj.attr('id');
var thisSubmit = obj.attr('data-submit');
var thisCallback = obj.attr('data-callback');
var thisUrl = $('#' + obj.attr('data-url')).text();
new AjaxUpload(thisId, {
action: thisUrl,
name: 'thisfile',
onSubmit: function() {
imageObject[thisSubmit](arg, anotherArg, etc);
},
onComplete: function() {
imageObject[thisCallback](arg, anotherArg, etc);
}
});
}
// Presumably there are more methods here, or added later...
}
you have to use call which call a function in js
onSubmit: function(file, extension) {
imageObject.thisSubmit.call(undefined, file,extension);
},
see What is the difference between call and apply?

Javascript callback within class loses binding

I have a problem with my class where I set a callback function to fire after an ajax request, this works fine except for the callback then having this= window instead of this = the calling class. I have tried fiddling with the bindings etc to no avail. any help appreciated.
<?php
if (isset($_POST['id'])){
echo json_encode(array('key' => 'val'));
die;
}
?>
<script type="text/javascript" src="/linkup/js/mootools-core-1.4.3-full-compat-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="/linkup/js/mootools-more-1.4.0.1.js" charset="utf-8"></script><script>
var myClass = new Class({
blah: function(){
console.log('this worked ok');
},
foo: function(){
this.blah(); // at this point it is failing with "unknown function"
},
bar: function(){
this.reqJSON({id:1,req:'grabpoints'},this.foo);
},
// data - an aray containing some data to post {id:1,req:'grabpoints'}
// onS - a function to fire on return of data
reqJSON: function(data,onS){
if (this.jsonRequest) this.jsonRequest.cancel();
this.jsonRequest = new Request.JSON({
url: 'test.php',
onRequest: function (object){
console.log('sending json');
},
onSuccess: function (object){
console.log('json success');
onS(object);
}
}
).post(data);
},
});
var o = new myClass();
o.bar();
this is never part of an inherited scope (i.e. the closure) for a function, but instead determined when the function is called, in this case by onS(object). Called this way, the function is just that - a function. You need to call this.onS(object), but this won't work in your case because the onSuccess function does not know about this either.
To call the foo/onS function with the outermost object as this you must save a reference to it in another variable, commonly called that:
reqJSON: function(data,onS){
var that = this;
...
onSuccess: function (object){
console.log('json success');
that.onS(object);
}
change this
bar: function(){
this.reqJSON({id:1,req:'grabpoints'},this.foo);
}
to :
bar: function(){
var self = this;
this.reqJSON({id:1,req:'grabpoints'},self.foo);
}

Javascript Object Literal Scope Issue?

I'm having a hard time understanding why this.$map and this.markers are undefined. Here's the code I'm working with and I have added comments where I expect these variables to supply a value:
(function($) {
'use strict';
var A = {
/**
* Initialize the A object
*/
init: function() {
this.$map = this.renderMap();
this.markers = this.getMarkers();
this.renderMarkers();
},
renderMap: function() {
var url = 'http://a.tiles.mapbox.com/v3/delewis.map-i3eukewg.jsonp';
// Get metadata about the map from MapBox
wax.tilejson(url, function(tilejson) {
var map = new L.Map('map', {zoomControl: false});
var ma = new L.LatLng(42.2625, -71.8028);
map.setView(ma, 8);
// Add MapBox Streets as a base layer
map.addLayer(new wax.leaf.connector(tilejson));
return function() {
return map;
};
});
},
renderMarkers: function() {
var geojsonLayer = new L.GeoJSON(null, {
pointToLayer: function (latlng){
return new L.CircleMarker(latlng, {
radius: 8,
fillColor: "#ff7800",
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
}
});
geojsonLayer.addGeoJSON(this.markers); // this.markers is undefined
this.$map.addLayer(geojsonLayer); // this.$map is undefined
},
getMarkers: function() {
$.getJSON("/geojson/", function (data) {
return data;
});
}
};
/**
* A interactions
*/
$(document).ready(function() {
A.init()
});
})(jQuery.noConflict());
I have spent much of the day searching and I think I'm missing something fundamental here, but I don't get it.
Neither the renderMap, nor getMarkers methods return any value, consequently their return value is undefined.
It looks like you are trying to initialize these fields from an ajax request, which is not necessarily a good idea.
What you probably ought to do is something like:
getMarkers: function(callback){
var result = this;
$.getJSON("url", function(jsonData){
result.markers = jsonData;
if(callback) callback()
});
},
which will lazily initialize the fields of the object as they become available.
NB: AJAX is asynchronous you cannot rely on this callback setting the member quickly, or indeed ever (it could fail).
This suggests you need to think a bit more about your design, and try to use callbacks more.
e.g. modify the getMarkers and renderMap functions as above to take a callback that is called after the data is stored then change init to:
init: function(){
var res = this;
var post_init_callback = function(){
if(res.$map != undefined && res.markers!=undefined){
//this will only run after both ajax calls are complete
res.renderMarkers();
}
};
this.getMarkers(post_init_callback);
this.renderMap(post_init_callback);
},
The problem here is that you call return inside another function. What you're essentially doing is defining getMarkers (the simplest example) as this:
getMarkers: function() {
$.getJSON('/geojson/', some_random_func);
}
At which point it becomes obious that getMarkers doesn't actually return anything (ergo undefined). The same goes for your renderMap function. Also in this case your "some_random_func" is defined as function(data) { return data; } but what does it return it to? Truth is that the some_random_func is called by jQuery itself, and AFAIK jQuery doesn't care at all for the return-value of it's success-function.

Hooking onto onSuccess in Prototype's AJAX

What is the safest way to make a snippet of code run at the beginning of every AJAX success callback? I'm creating an time-sensitive website and would like each AJAX to synch the user's clock with the server's clock. Below is an example of the code I'd like to run. I'd rather not have to duplicate this code across all my AJAX (even if it was made into a function).
new Ajax.Request(
'someLink.php', {
onSuccess: function(transport) {
var dateHeader = transport.getHeader('Date');
if (dateHeader === null) {
USER.serverDrift = 0;
} else {
USER.serverDrift = (new Date(dateHeader).getTime()
- (new Date().getTime())) / 1000;
}
// application specific code here
}
}
);
One way would be to wrap the code want to run in a function and then have that function call your onSuccess code. Obviously this wrapped function would have to be returned from a function:
var checkDriftOnSuccess = function(onSuccess) {
return function(transport) {
var dateHeader = transport.getHeader('Date');
if (!dateHeader) {
USER.serverDrift = 0;
} else {
USER.serverDrift = (new Date(dateHeader).getTime()
- (new Date().getTime())) / 1000;
}
onSuccess(transport);
};
};
And you'd call it like this:
new Ajax.Request(
'someLink.php', {
onSuccess: checkDriftOnSuccess(function(transport) {
// application specific code here
})
}
);
UPDATE: another way, by replacing the Prototype constructor. Not favored by me (a) because I don't know the Prototype codebase, and (b) there may be instanceOf issues, and (c) I haven't actually tested it.
var ajaxReplacer = (function() {
var oldRequest;
var checkDriftOnSuccess = function(onSuccess) {
return function(transport) {
var dateHeader = transport.getHeader('Date');
if (!dateHeader) {
USER.serverDrift = 0;
} else {
USER.serverDrift = (new Date(dateHeader).getTime()
- (new Date().getTime())) / 1000;
}
onSuccess(transport);
};
};
// save the real constructor
oldRequest = Ajax.Request;
// define a new constructor that wraps the onSuccess callback
Ajax.Request = function(url, options) {
if (options.onSuccess) {
options.onSuccess = checkDriftOnSuccess(options.onSuccess);
}
// call the real constructor
return new oldRequest(url, options);
// alternately, call the real constructor using a function call
// return oldRequest.call(this, url, options);
};
Ajax.Request.prototype = oldRequest.prototype;
return {
reset: function() {
Ajax.Request = oldRequest;
}
};
}());
In essence it creates a new object called ajaxReplacer. This has one method, reset that returns everything to the default Prototype behavior (a one-shot deal). Creating the object will replace the ajax request constructor to one that calls the special code in onSuccess.
Note I have two ways of calling the old code. I do not know which one will work properly since I don't use Prototype. Test, test, and test again. If it works, great. If it doesn't, maybe I forgot to do something, or it gives you enough ideas to really do it, or you just can't do this easily.

Categories

Resources