JS Prototype scope with this/bind - javascript

I have the following code that creates an object in JavaScript. It uses prototype to define functions and constructors.
function objectClass(){
this.variables = new Array();
}
objectClass.prototype =
{
contructor: objectClass,
setInfo: function(){
$.ajax({
url: "info.json",
success: function(){
//for each json element returned...
this.variables.push(json[i]);
}
});
}
getInfo: function(){
return this.variables;
},
}
This is a similar example of what I am trying to do. I need to be able to return the array of variables when I call obj.getInfo(). It always throws an error. I believe it is because the "this" is referring to the scope of the ajax success function.
Any ideas on how to get it to reference the objects variable?

That's correct, the this value is not automatically passed and thus not set to the instance. To force this, you can use the context property that $.ajax accepts:
$.ajax({
context: this, // `this` is the instance here
This sets the this value inside the success callback to the one you specified.

Related

How to callback `this` within ajax function

So I want to use this from an outer function within an ajax success function. I tried to apply these solutions but somehow I can't bring it to work.
// Submit vote on submit
$('.vote-choice-div').on('click', function(event){
event.preventDefault();
// bind the clicked object
this.submit_vote.bind(this); // so I have to bind the element to use it in the ajax function?
// fire ajax
submit_vote(this.id, this);
});
// AJAX for posting
function submit_vote(vote) {
$.ajax({
url : "submit-vote/",
headers: {'X-CSRFToken': csrftoken},
type : "POST",
data : { vote : vote },
success : function(data) {
if(data.status === 1){
console.log(this) // can't access the initial clicked element
}
Uncaught TypeError: Cannot read properties of undefined (reading 'bind')
You have two problems (and a pointless argument).
submit_vote is a global, not a property of the element. To access it, you don't use this.
bind returns a new function. It doesn't mutate the existing one
submit_vote only accepts one argument
So:
const localSubmitVote = submit_vote.bind(this)
localSubmitVote(this.id);
However… bind is only useful if you are going to store a function so you can pass it around or use it multiple times.
You aren't doing that, you're only calling it once, so use call
submit_vote.call(this, this.id);
However… submit_vote isn't a method. It isn't sensible to design it to use this in the first place. So the better approach here is to redesign it to just use the second argument that you were passing before.
function submit_vote(vote, element) {
$.ajax({
// ...
success: function(data) {
if (data.status === 1) {
console.log(element);
}
}
});
}
and
submit_vote(this.id, this)

Variables in js

I am quite a noob in regards to js and today I come across bit of js I don't really understand and I would like to. Could you please shed some light ? There is Kendo involved but the question is plain js.
I have a nested grid, eg. each row can expand into other grid and each of this grid has its own datasource. I populate the datasources via the method below one by one as user clicks and I had a problem with forcing the datasource to read when it receives async response from the create call. (calling the read is pretty much incorrect thing to do, but Kendo has its own bugs - not a point here.). My problem was, I didn't have an instance to call the read() on, the method only returns datasource and assigns it to a grid, when the event comes back I can't find any reference to anything I could get the correct datasource instance from. this is different context in here.
In order to resolve this I added a datasource variable into the method what builds the datasource and I return the variable instead the datasource, which is the same thing. However this helps to have something to call the problematic read() on. Now in my create handler I call create on the variable I am returning in the method during innit. Well it works, but I am not sure if every datasource is calling read on its own instance after innit ?
function _getDatasource() {
var datasource = new kendo.data.DataSource({
transport: {
read: {
url: serviceBaseUrl + "ReadQuestionnaire",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
},
create: {
url: serviceBaseUrl + "CreateQuestionnaire",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
complete: function (jqXhr, textStatus) {
if (CheckForExceptions(jqXhr, textStatus) == false) {
// this is the variable I am not sure about
// after innit does this always refers to this same datasource ?
datasource.read();
}
}
}
}
});
return datasource;
}
Your solution is correct, and yes, the datasource.read() call is the correct datasource object in each case.
Here's why this works: closures.
A closure is the ability to have a variable declared in one function accessible from a nested function. Or, for a more accurate description, see the wikipedia page: http://en.wikipedia.org/wiki/Closure_(computer_science)
Here's a very simple example:
function doStuff(){
var name = "Derick";
function sayMyName(){
console.log(name);
}
sayMyName();
}
doStuff();
In this example, I'm declaring a variable name inside of the doStuff function. Then I'm nesting another function inside of the first function. The sayMyName function is accessing the name variable through the use of a closure.
When I call doStuff(), the variable is defined and assigned to a value. Then the sayMyName function is defined and it uses that variable. I then call sayMyName() and it logs the name to the console.
Similarly, in your code you are creating a variable that is assigned to the instance of the DataSource. Later, you are defining a function for the complete callback. After the data source has loaded and the complete callback is fired, you are accessing the same dataSource variable that you had assigned to the DataSource instance through the use of a closure around that variable.
Since you are declaring var dataSource every time you call _getDataSource, you are creating a new variable / reference, assigned to a new DataSource instance. I don't think you need to return datasource at the bottom of your function, though... at least, not for the complete callback function to work. Maybe you need it for something else, outside of this function, though?
For more information on closures in JavaScript:
https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures
How do JavaScript closures work?
http://www.javascriptkit.com/javatutors/closures.shtml
http://www.watchmecode.net/javascript-scope (paid screencast)
HTH

Accessing the calling object into ajax response... (not the ajax call)

I have an object of type Application (defined by me). Whenever an object of this type is created, it automatically loads a php file say "start.php" using jquery ajax and assign the response to a div say "Respo". Now what i want is to access the Application object from that Respo div. Unfortunately, i have no clue how to do this...
in my ajax call:
function Application(options)
{
.......
var appObj=this;
$.ajax({
url:appObj.location, //Already defined
success:function(data)
{
$("#respo").html(data);
}
});
}
Now in my Respo division i want to access that Application object... I tried:
alert(this)
but it resulted in an object of DOMWindow...
i tried editing success function as:
function Application(options)
{
.......
var appObj=this;
$.ajax({
url:appObj.location, //Already defined
success:function(data)
{
$("#respo").html("<script type='text/javascript'>var Self="+appObj+"</script>");
$("#respo").html(data);
}
});
}
But i ended nowhere. :( Although if i assign "var Self='nishchay';" then alerting Self from start.php gives nishchay but i am not able to assign the calling object of Application type to the Self variable. It is the only way I cud think of. :\
Please help me... actually my object has some editing functions to control itself - its look and feel and some other options. I want the code loaded by object to control the object itself.
Please help me..
Thanks in advance.
Nishchay
You can pass "this" as the "context" property (jQuery1.4) in the $.ajax then you can access that inside the "success" callback simply as "this", below is what jQuery doc says:
This object will be made the context
of all Ajax-related callbacks. For
example specifying a DOM element as
the context will make that the context
for the complete callback of a request
Here is code example:
function Application(options)
{
.......
var appObj=this;
$.ajax({
url:appObj.location, //Already defined
context: this,
success:function(data)
{
console.log( this ); // will be pointing to the object that you passed as the value of the "context" property
// YOU SHOULD NOT BE USING THINGS LIKE BELOW
//$("#respo").html("<script type='text/javascript'>var Self="+appObj+"</script>");
$("#respo").html(data);
}
});
}
Hope it helps.
var appObjPool = {};
var appObjID = 'xxx';
function Application(options)
{
.......
appObjPool[appObjID] = appObj;
var appObj=appObjPool[appObjId];
$.ajax({
url:appObj.location, //Already defined
success:function(data)
{
$("#respo").html("var Self=appObjPool['"+appObjID+"']");
$("#respo").html(data);
}
});
}

Unable to use "class" methods for callbacks in JavaScript

I'm having a really rough time wrapping my head around prototypes in JavaScript.
Previously I had trouble calling something like this:
o = new MyClass();
setTimeout(o.method, 500);
and I was told I could fix it by using:
setTimeout(function() { o.method(); }, 500);
And this works. I'm now having a different problem, and I thought I could solve it the same way, by just dropping in an anonymous function. My new problem is this:
MyClass.prototype.open = function() {
$.ajax({
/*...*/
success: this.some_callback,
});
}
MyClass.prototype.some_callback(data) {
console.log("received data! " + data);
this.open();
}
I'm finding that within the body of MyClass.prototype.some_callback the this keyword doesn't refer to the instance of MyClass which the method was called on, but rather what appears to be the jQuery ajax request (it's an object that contains an xhr object and all the parameters of my ajax call, among other things).
I have tried doing this:
$.ajax({
/* ... */
success: function() { this.some_callback(); },
});
but I get the error:
Uncaught TypeError: Object #<an Object> has no method 'handle_response'
I'm not sure how to properly do this. I'm new to JavaScript and the concept of prototypes-that-sometimes-sort-of-behave-like-classes-but-usually-don't is really confusing me.
So what is the right way to do this? Am I trying to force JavaScript into a paradigm which it doesn't belong?
Am I trying to force JavaScript into a paradigm which it doesn't belong?
When you're talking about Classes yes.
So what is the right way to do this?
First off, you should learn how what kind of values the this keyword can contain.
Simple function call
myFunc(); - this will refer to the global object (aka window) [1]
Function call as a property of an object (aka method)
obj.method(); - this will refer to obj
Function call along wit the new operator
new MyFunc(); - this will refer to the new instance being created
Now let's see how it applies to your case:
MyClass.prototype.open = function() {
$.ajax({ // <-- an object literal starts here
//...
success: this.some_callback, // <- this will refer to that object
}); // <- object ends here
}
If you want to call some_callback method of the current instance you should save the reference to that instance (to a simple variable).
MyClass.prototype.open = function() {
var self = this; // <- save reference to the current instance of MyClass
$.ajax({
//...
success: function () {
self.some_callback(); // <- use the saved reference
} // to access instance.some_callback
});
}
[1] please note that in the new version (ES 5 Str.) Case 1 will cause this to be the value undefined
[2] There is yet another case where you use call or apply to invoke a function with a given this
Building on #gblazex's response, I use the following variation for methods that serve as both the origin and target of callbacks:
className.prototype.methodName = function(_callback, ...) {
var self = (this.hasOwnProperty('instance_name'))?this.instance_name:this;
if (_callback === true) {
// code to be executed on callback
} else {
// code to set up callback
}
};
on the initial call, "this" refers to the object instance. On the callback, "this" refers to your root document, requiring you to refer to the instance property (instance_name) of the root document.

Scoping inside Javascript anonymous functions

I am trying to make a function return data from an ajax call that I can then use. The issue is the function itself is called by many objects, e.g.:
function ajax_submit (obj)
{
var id = $(obj).attr('id');
var message = escape ($("#"+id+" .s_post").val ());
var submit_string = "action=post_message&message="+message;
$.ajax({
type: "POST",
url: document.location,
data: submit_string,
success: function(html, obj) {
alert (html);
}
});
return false;
}
Which means that inside the anonymous 'success' function I have no way of knowing what the calling obj (or id) actually are. The only way I can think of doing it is to attach id to document but that just seems a bit too crude. Is there another way of doing this?
You can use variables from the enclosing scope, a technique called "closure". So:
function ajax_submit (obj)
{
var id = $(obj).attr('id');
var message = escape ($("#"+id+" .s_post").val ());
var submit_string = "action=post_message&message="+message;
$.ajax({
type: "POST",
url: document.location,
data: submit_string,
success: function(html) {
alert(obj.id); // This is the obj argument to ajax_submit().
alert(html);
}
});
return false;
}
If you are attempting to load html onto the page via ajax you may want to consider the load() function.
Functions in JavaScript become enclosed in the scope in which they are defined (this is a closure). In this case, a new anonymous success callback function is created every time ajax_submit() is called, so all the variables from the parent scope will always be accessible.
Your code should work just fine as is. If you want to have a callback function, it can be passed as an argument to ajax_submit() and called like this:
…
success: function(html, obj) {
callback(html);
}
…
The variables obj, id and message are all available within the anonymous function.
This is because of a feature in Javascript called closures, which I advise you read up on.
The general gist of a closure is that a function will forever have access to the variables that were present in the scope it was defined in.
The result of this is that you can do:
success: function(html) {
alert (id);
alert (obj);
}
all day long (but note that the obj parameter in the success function will take precedence over the obj variable in your ajax_submit function.)

Categories

Resources