function call with this keyword is undefined - javascript

I am calling local (class) function via this pointer, but get an error 'Uncaught TypeError: undefined is not a function'. Probem occur on line
this.createtimetable(); at loadtimetable function.
My JS (relevant) is :
this.createtimetable = function () {
this.inside_timetable = [];
for (var d = new Date(in_week_start); d <= new Date(in_week_end); d.setDate(d.getDate() + 1)) {
console.log(new Date(d));
daysOfYear.push(new Date(d));
}
}
this.loadtimetable = function (in_guide_id, in_week_start, in_week_end) {
this.guide_id = in_guide_id;
this.week_start = in_week_start;
this.week_end = in_week_end;
$.post("./j.php", {
guide_id : in_guide_id,
week_start : in_week_start,
week_end : in_week_end
})
.done(function (data) {
var res_arr = jQuery.parseJSON(data);
if (res_arr.code == 0) {
this.excursions_base = res_arr.answer;
alertify.success("Data extracted");
this.createtimetable();
} else {
alertify.error("Some problem occured." + data);
}
}).fail(function () {
alertify.alert("Error. Please, refresh page, or try later. We are sorry. Write or call us with your question!");
});
}
Calling by name (i.e. createtimetable() ) also fail. Thank you for ideas!

Your code is executed in a callback, and this no longer points to your object. You should either use a closure, aliasing this to something like self, or explicitly bind this
this.createtimetable = function () {
this.inside_timetable = [];
for (var d = new Date(in_week_start); d <= new Date(in_week_end); d.setDate(d.getDate() + 1)) {
console.log(new Date(d));
daysOfYear.push(new Date(d));
}
}
this.loadtimetable = function (in_guide_id, in_week_start, in_week_end) {
this.guide_id = in_guide_id;
this.week_start = in_week_start;
this.week_end = in_week_end;
$.post("./j.php", {
guide_id: in_guide_id,
week_start: in_week_start,
week_end: in_week_end
})
.done(function (data) {
var res_arr = jQuery.parseJSON(data);
if (res_arr.code == 0) {
this.excursions_base = res_arr.answer;
alertify.success("Data extracted");
this.createtimetable();
} else {
alertify.error("Some problem occured." + data);
}
}.bind(this)).fail(function () {
alertify.alert("Error. Please, refresh page, or try later. We are sorry. Write or call us with your question!");
}.bind(this));
}

Store reference of $(this)outside of post function call ans use it in done callback function, here this doesn't refers to your object.
this.loadtimetable = function(in_guide_id, in_week_start, in_week_end)
{
var self = this; //store reference of this
$.post( "./j.php", {})
.done(function( data ) {
self.createtimetable(); //Here instead of this use your variable
});
}
EDIT
If you are open to use $.ajax() instead of $.post(). You can use the context option.
This object will be made the context of all Ajax-related callbacks. By default, the context is an object that represents the ajax settings used in the call ($.ajaxSettings merged with the settings passed to $.ajax). (...)
$.ajax({
context: this
});

Related

Javascript. Passing an instance of an object as an argument, inside a new operator

So I'm trying to refactor a function that goes something like this:
if (typeof init_common_load_more != "function") {
init_common_load_more = function (id) {
fdxm.js_manager.on("fdx_loadmore_js", function () {
component_wrapper = document.getElementById(id);
const loader = new fdxLoadMore({
posts_counter:
component_wrapper.querySelector(".posts_counter"),
load_more_posts_url: function (instance) {
let post_list_obj = instance.getObject("posts_list"),
load_options = {
controller:
post_list_obj.dataset["load_more_controller"],
//...other options
};
let url = "/api/posts/load_more_posts";
for (option in load_options) {
url += "/" + option + "/" + load_options[option];
}
return url;
},
});
// do things wiht the loader
});
};
}
This somehow works. The problem arises when I try to make the "load_more_posts_url" function a separate function:
if (typeof init_common_load_more != "function") {
init_common_load_more = function (id) {
fdxm.js_manager.on("fdx_loadmore_js", function () {
component_wrapper = document.getElementById(id);
const loader = new fdxLoadMore({
posts_counter:
component_wrapper.querySelector(".posts_counter"),
load_more_posts_url: get_load_more_url(this),
});
});
};
// do things wiht the loader
function get_load_more_url(instance) {
let post_list_obj = instance.getObject("posts_list"),
load_options = {
controller: post_list_obj.dataset["load_more_controller"],
//...other options
};
let url = "/api/posts/load_more_posts";
for (option in load_options) {
url += "/" + option + "/" + load_options[option];
}
return url;
}
}
I passed "this" as a parameter, because sometimes writing random words makes the code work. But not this time: when I console log "instance" I'm getting some kind of default callback object instead of the new fdxLoadMore object.
How can I reference the new fdxLoadMore, in the same way it is referenced on the first version of the code, but using a separate, named function?

Generating an array of distinct callback functions in Javascript

I'm trying to generate an array of callback functions for use in a jQuery UI dialog
Given the following code:
for(var x in methods)
{
buttons[x] = function() {
var method = methods[x];
var data = $('#dialog_'+model+' form').serialize();
data += '&form='+model;
$.post(
$('#dialog_'+model+' form').attr('action')+'method/'+method+'/',
data,
function(r) {
handleFormReturn(r);
},
'json'
);
};
}
When called, the function will obviously use the last known value of the variable x and not the one that I need. How can I avoid this problem without having to resort to using eval() ?
Maybe I'm going about this all wrong but as far as I know it's not possible to pass a parameter to the callback.
You need to create a new variable scope during each pass in the for loop. This can only be done by invoking a function.
function createButton(x) {
buttons[x] = function () {
var method = methods[x];
var data = $('#dialog_' + model + ' form').serialize();
data += '&form=' + model;
$.post(
$('#dialog_' + model + ' form').attr('action') + 'method/' + method + '/', data, function (r) {
handleFormReturn(r);
}, 'json');
};
}
for (var x in methods) {
createButton(x);
}
Now the value of x that the buttons[x] function refers to will be the one that was passed to createButton.
An immediate function version of patrick dw's solution:
for (var x in methods) {
buttons[x] = (function (x) {
return function () {
/* ... x is local for this function ... */
};
})(x);
}
You need to create a closure for each element in methods array:
for(var x in methods) {
buttons[x] = (function(x) {
var method = methods[x];
return function () {
var data = $('#dialog_'+model+' form').serialize();
data += '&form='+model;
$.post(
$('#dialog_'+model+' form').attr('action')+'method/'+method+'/',
data,
function(r) {
handleFormReturn(r);
},
'json'
);
};
})(x);
}

Why has the author of this code used the .call method? Surely he could have just accessed the prototype?

Im looking through some code (unfortunatly the author isnt around anymore) and im wondering why he has used the .call method.
hmlPlaylist.prototype.loadVideos = function () {
var scope = this;
this.config.scriptUrl = '_HMLPlaylistAjax.aspx?' + Math.random();
jQuery.ajax({
type: 'GET',
url: this.config.scriptUrl,
success: function (d, t, x) {
scope.loadVideos_callback.call(scope, d);
},
error: function () {
}
});
};
hmlPlaylist.prototype.loadVideos_callback = function (data) {
var jsonData = '';
var jsonError = false;
try {
jsonData = eval("(" + data + ")");
} catch (jError) {
jsonError = true;
}
if (!jsonError) {
if (jsonData.playlists.length > 0) {
this.buildPlaylistList(jsonData.playlists);
}
if (jsonData.videos.length > 0) {
this.buildVideoList(jsonData.videos);
this.bindVideoNavs();
}
}
else {
// no json returned, don't do anything
}
};
Obviously he seems to have used it to pass a 'this' reference to the loadVideos_callback method but why? The 'loadVideos_callback' method is attached to the prototype of 'hmlplaylist' which is the 'class'. So if you access this inside the 'loadVideos_callback' method you get to the same thing dont you?
yes, I think you are right (I can't see the code in action). You still need the closure around scope, but in this case the use of call is not necessary.
To pull some of the comments into this answer, this is always the context on which the method was invoked. So if a new instance of htmlPlayList was created, and the method invoked on that instance, this would be a reference to that instance.

Resolve function pointer in $(document).ready(function(){}); by json string name

I have a json object retrieved from server in my $(document).ready(...); that has an string that I would like to resolve to a function also defined within $(document).ready(...); so, for example:
$(document).ready(function{
$.getJSON(/*blah*/,function(data){/*more blah*/});
function doAdd(left,right) {
return left+right;
}
function doSub(left,right) {
return left-right;
}
});
with json string:
{"doAdd":{"left":10,"right":20}}
One way I thought about was creating an associative array of the function before loading the json:
var assocArray=...;
assocArray['doAdd'] = doAdd;
assocArray['doSub'] = doSub;
Using eval or window[](); are no good as the function may not be called for some time, basically I want to link/resolve but not execute yet.
Change your JSON to
{method: "doAdd", parameters : {"left":10,"right":20}}
Then do
var method = eval(json.method);
// This doesn't call it. Just gets the pointer
Or (haven't tried this)
var method = this[json.method]
How about something like this?
$(function(){
// Function to be called at later date
var ressolvedFunc = null;
// Ajax call
$.getJSON(/*blah*/,function(data){
// Generate one function from another
ressolvedFunc = (function(data) {
var innerFunc;
var left = data.left;
var right = data.right;
// Detect action
for (action in data) {
if (action == "doAdd")
innerFunc = function() {
return left + right;
};
else
innerFunc = function() {
return left - right;
};
}
return innerFunc;
})(data);
});
});
The anonymous function returns fresh function, with the new values stored within the enclosure. This should allow you to call the function at later date with the data previously retrieved from the GET request.
Rich
try this:
var doX = (function() {
var
data = [],
getDo = function(action) {
for(var d in data) {
if (data[d][action]) {
return data[d];
}
}
return null;
};
return {
set: function(sdata) {
data.push(sdata);
},
doAdd: function() {
var add = getDo("doAdd");
if (!add)
return 0;
return add.doAdd.left + add.doAdd.right;
},
doSub: function() {
var sub = getDo("doSub");
if (!sub)
return 0;
return sub.doAdd.left + sub.doAdd.right;
}
};
})();
$(document).ready(function{
$.getJSON(/*blah*/,function(data){ doX.set(data); });
});

Call a javascript function from outside its object

I have some simple javascript that as far as i can tell should work but doesn't.
The code is below
var presenter = new Practicum.Web.TEI.StudentPlacement2009.CreateLetter_class(); //this is a class generated by Ajax.Net
function GetLetters() {
var GetLettersParams = new Object();
GetLettersParams.TemplateType = $('#LetterTypes').val();
var letters = ajaxCall(presenter.GetLetters, GetLettersParams);
createOptions('Templates', letters, 'Id', 'Name', true);
}
function ajaxCall(ajaxMethod, parameters) {
var response = ajaxMethod.call(parameters); //fails here with the message in
if (response.error != null) {
alert('An error has occured\r\n' + response.error.Message);
return;
}
return response.value;
}
this is part of the class Ajax.Net produces.
Practicum.Web.TEI.StudentPlacement2009.CreateLetter_class = function() {};
Object.extend(Practicum.Web.TEI.StudentPlacement2009.CreateLetter_class.prototype, Object.extend(new AjaxPro.AjaxClass(), {
GetLetterTypes: function() {
return this.invoke("GetLetterTypes", {}, this.GetLetterTypes.getArguments().slice(0));
},
GetDegrees: function() {
return this.invoke("GetDegrees", {}, this.GetDegrees.getArguments().slice(0));
},
GetLetters: function(getLettersParams) {
return this.invoke("GetLetters", {"getLettersParams":getLettersParams}, this.GetLetters.getArguments().slice(1));
} ...
Any help would be much appriciated;
Colin G
The first parameter that needs to be passed to Function.call() is the object on which the function is called. Then follow the function parameters as separate values:
func.call(someobj, param1, param2, ...);
To call a function with an array of arguments you should use apply(). apply() also takes the object for which the method should be called as first parameter:
func.apply(someobj, params);
So in your case it would look something like this:
function ajaxCall(ajaxMethod, obj, parameters) {
var response = ajaxMethod.call(obj, parameters);
// ...
}
var letters = ajaxCall(presenter.GetLetters, presenter, GetLettersParams);
You need to pass an object to the first argument of the call method e.g.:
ajaxMethod.call(presenter, parameters);
See http://www.webreference.com/js/column26/call.html
Supertux is right. You could try this to make sure the context is set for "call":
function GetLetters() {
var GetLettersParams = new Object();
GetLettersParams.TemplateType = $('#LetterTypes').val();
var letters = ajaxCall(presenter.GetLetters, presenter, GetLettersParams);
createOptions('Templates', letters, 'Id', 'Name', true);
}
function ajaxCall(ajaxMethod, context, parameters) {
var response = ajaxMethod.call(context, parameters); //Call requires a context
if (response.error != null) {
alert('An error has occured\r\n' + response.error.Message);
return;
}
return response.value;
}
Or you could simplify things quite a bit by not using ajaxCall:
function GetLetters() {
var GetLettersParams = {
TemplateType: $('#LetterTypes').val()
},
response = presenter.GetLetters(GetLettersParams);
if (response.error != null) {
alert('An error has occured\r\n' + response.error.Message);
return;
}
createOptions('Templates', response.value, 'Id', 'Name', true);
}

Categories