1

I can't access the attribute of an instantiated class. The attribute is set using an AJAX call.

I am trying to define the class "CurrentUser", and then set the attribute "userId" using AJAX.


Here I define the class CurrentUser, and give it the attribute userID:

function CurrentUser() {
    // Do an ajax call to the server and get session data.
    $.get("../../build/ajaxes/account/get_user_object_data.php", function(data) {
         this.userId = data.userId;
         console.log(data.userId);   // This will correctly output "1".
    }, "JSON");
}


Here I instantiate a CurrentUser named billybob. Notice how I can't output billybob's attribute:

// Instantiate the user. 
   var billybob = new CurrentUser();
   console.log(billybob.userId);     // This will incorrectly ouput "undefined".



I've checked the common errors with AJAX:

  • The AJAX call returns the data correctly as a JSON object. I can read the correct object in Firebug / Network console. The AJAX call also has a status of "200" and "OK".

  • I can log the AJAX call's data correctly, as seen in the first part of my code where I log data.userId.

Don P
  • 60,113
  • 114
  • 300
  • 432
  • 1
    I was thinking, maybe it's possible that "this.userId" is not referring to the object, but instead to the AJAX call? – Don P Nov 27 '11 at 18:21
  • Even if `this` referred to what you wanted it to, `new CurrentUser()` would return before the Ajax method did. – Dave Newton Nov 27 '11 at 18:26

2 Answers2

2

Maybe this clears it out:

In your original code:

function CurrentUser() {
    // Do an ajax call to the server and get session data.
    $.get("../../build/ajaxes/account/get_user_object_data.php", function(data) {
     this.userId = data.userId;
     console.log(data.userId);   // This will correctly output "1".
    }, "JSON");
}

You are creating an anonymous function on the fly, that will be later called by jQuery's internals with this set to an ajax object. So this will be the ajax object inside the anonymous function, not billybob. So when you do this.userId = ... this means the ajax object which doesn't have a userid property.

jQuery will have no idea where you got your callback function from, so it cannot set the this automagically for you.

What you must do is to save the billybob (or any CurrentUser instance) reference and use it in the callback like so:

function CurrentUser() {
var self = this;
    $.get("../../build/ajaxes/account/get_user_object_data.php", function(data) {
     self.userId = data.userId; //self refers to what this refered to earlier. I.E. billybob.
     console.log(data.userId, self.userid);   // This will correctly output "1".
    }, "JSON");
}

Also note that:

 var billybob = new CurrentUser();
   console.log(billybob.userId);   

By the time you call console.log (I.E. instantly after creating billybob), the ajax request hasn't been completed yet so it is undefined.

Esailija
  • 138,174
  • 23
  • 272
  • 326
  • still doesn't work unfortunately: `function CurrentUser() { var self = this; $.get("../../build/ajaxes/account/get_user_object_data.php", function(data) { self.userId = data.userId; //self refers to what this refered to earlier. I.E. billybob. console.log(data.userId, self.userId); // This will correctly output "1". }, "JSON"); console.log(self.userId); }` – Don P Nov 27 '11 at 19:46
  • Outside of the $.get, self.userId is still undefined. Do I need to declare self.userId as global or something before setting self.userId = data.userId? – Don P Nov 27 '11 at 19:47
  • @DonnyP, do you see in console: `"1 1"` instead of just `"1"`? Then it's working. The `console.log(billybob.userId)` doesn't work for reasons I outlined in my answer. No, you just need to try logging after the answer is completed. Inside the $.get callback you could call another function that logs it or something. Because when that function is called you know for sure the request is completed. – Esailija Nov 27 '11 at 19:47
  • ahhh I see. So that also explains why my console is logging the AJAX call (script ln. 10) AFTER my other undefined console logs (script ln. 30 and script ln. 55). Ok let me check it over for a second - thank you. – Don P Nov 27 '11 at 19:57
  • @DonnyP Here's simple example: http://jsfiddle.net/4btEN/2/. It is effectively the same thing, you cannot do anything with the user until the response is ready. – Esailija Nov 27 '11 at 19:59
  • Ok looks good - final question. If I want to display elements of my page (a user profile picture, a username in the top right corner like Facebook, etc.) I can't use my current approach because of the wait for the AJAX to complete. I would have to execute my entire javascript as a callback to this AJAX function, and that would be some really odd programming. – Don P Nov 27 '11 at 20:02
  • @DonnyP, you just tie the profile rendering to the ajax call (or parts of the profile that require the info), not your whole app. Alternatively you could not do the ajax at all and just include the profile already from server code. – Esailija Nov 27 '11 at 20:07
0

In constructor, consider doing

var self = this;

and in function

self.userId = data.userId;

this inside function will be different than outside. Although, I don't know JQuery. May be it's supposed to set up closure automatically.

Alex Gitelman
  • 24,429
  • 7
  • 52
  • 49
  • what is the difference between using "self.userId" and "this.userId"? I am used to PHP where self is for static attributes, and this is for non-static attributes. Also doesn't seem to change the outcome unfortunately. – Don P Nov 27 '11 at 18:31
  • In Javascript `this` has a different meaning than in other languages. It defines a scope rather than specific object. Therefore, in function it will not refer to the object you are creating. But if you assign it to variable when `this` still refers to the right object, you can use this variable later on. It's one of trickiest JS concepts to me (I am new to JS myself). – Alex Gitelman Nov 27 '11 at 18:41
  • @AlexGitelman, `this` does not define a scope or anything like that, you can read [my answer here](http://stackoverflow.com/questions/8280137/defining-an-implementation-independent-version-of-the-global-object-in-javascrip/8280161#8280161) to get a better understanding. It's not magical at all, it's just a reference to an object (under non-strict mode) – Esailija Nov 27 '11 at 18:46
  • @Esailija As I said, I am new to JS. It's not magical - it's tricky for someone new. I misspoke about scope which just highlights the trickiness of the concept. One way I was explained `this` was that it's object that is before `.` when function is called or global object. Your referred answer is very good and I appreciate you pointing it. My answer is correct solution for specific question. I hope that you agree. – Alex Gitelman Nov 27 '11 at 19:02
  • @AlexGitelman, sorry my intent wasn't to say you were wrong. You mentioned you were new to js so I wanted to help you with what helps me to understand `this`. Basically just remembering that `this` is decided when the function is actually called helps a lot. Like in the OP's example, the callback function he made isn't called in his code so he can't predict what `this` will be inside it unless he knows jQuery calls it with the ajax object set explicitly by `call/apply`. – Esailija Nov 27 '11 at 19:03
  • @Esailija +1 on your answer. It straitens few things in my head :) – Alex Gitelman Nov 27 '11 at 19:05
  • @Esailija so it sounds like my code is correct? I set `this.userId` when the function is called, correct? So the ajax sets the values right when `billybob = new User();` is executed. – Don P Nov 27 '11 at 19:19
  • @DonnyP, no, jQuery has no idea you called it inside a `CurrentUser` instance so it can't set that context for your callback. You must save the `this` reference to any variable of your choice (`self` is popular, but it could be any legit variable name, self is not special in any way in this situation, it's just a good name for it) earlier on, when you still know what `this` is. As in, exactly what this answer suggest. – Esailija Nov 27 '11 at 19:22
  • No the function will be called somewhere else, it has no association with `billybob`. – Alex Gitelman Nov 27 '11 at 19:24
  • Ok so here is the constructor: `function CurrentUser() { var self = this; $.get("../../build/ajaxes/account/get_user_object_data.php", function(data) { self.userId = data.userId console.log(self.userId); }, "JSON"); } ` – Don P Nov 27 '11 at 19:36
  • And here is the function that creates the object: ` var billybob = new CurrentUser(); console.log(billybob.userId); ` – Don P Nov 27 '11 at 19:37
  • Yes. That's exactly what I suggest in my answer. – Alex Gitelman Nov 27 '11 at 19:37
  • doesn't work though, the console.log will still return 'undefined' – Don P Nov 27 '11 at 19:38
  • @DonnyP, because you are trying to log it before the request completed... the logging is instant but the ajax request isn't. Another words are synchronous and asynchronous :D – Esailija Nov 27 '11 at 19:40