Reputation: 3491
I'm trying to create an object called List. This object has a method add which simply pushes a task object onto this tasks array. I also built a load method to load items from a url.
My issue is I can't seem to reference the add method from within the load method, I get the following error:
Uncaught TypeError: Object # has no method 'add'.
How do I reference the add method from within the load method? The code I am using is below.
function List(){
this.tasks = new Array();
this.add = function(taskItem){
this.tasks.push(taskItem);
};
this.load = function(url){
$.getJSON(
url,
function(data){
$.each(data, function(key,val){
var task = new Task({
id:val.pkTaskId,
title:val.fldName,
status:val.fldStatus
});
this.add(task);
});
}
);
}
}
var userList = new List();
userList.load(url)
Upvotes: 0
Views: 287
Reputation: 91667
The context for jQuery Ajax callbacks is an object that represents the options used to make the Ajax request. That is, the options object passed to the call to $.ajax(options)
, merged with $.ajaxSettings
. You can override the context by setting the context
option. This means calling $.ajax()
instead of $.getJSON()
.
$.ajax({
context: this,
url: url,
dataType: 'json',
success: callback
});
Edit: Not sure why I was downvoted. It works the way I said. Try it here:
const ajaxOpts = {
url: URL.createObjectURL(new Blob([null], { type: "application/json" })),
dataType: 'json',
success(data) {
console.log(`this.foo(): '${this.foo()}'`);
},
}
$.ajax(ajaxOpts);
$.ajax({
...ajaxOpts,
context: {
foo() {
return "bar";
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
So, plugging this into the code from the question should work:
function List(){
this.tasks = new Array();
this.add = function(taskItem){
this.tasks.push(taskItem);
};
this.load = function(url){
$.ajax({
context: this,
url: url,
dataType: 'json',
success: function(data){
$.each(data, function(key,val){
var task = new Task({
id:val.pkTaskId,
title:val.fldName,
status:val.fldStatus
});
this.add(task);
});
},
});
}
}
var userList = new List();
userList.load(url)
Upvotes: 2
Reputation: 1132
To build off of Tomalak's answer, you could move the declaration of "self" to the main object level. This has proven to be pretty useful in the case of using this within nested object functions.
function List(){
var self = this;
self.tasks = new Array();
self.add = function(taskItem){
self.tasks.push(taskItem);
};
self.load = function(url){
$.getJSON(
url,
function(data){
$.each(data, function(key,val){
var task = new Task({
id:val.pkTaskId,
title:val.fldName,
status:val.fldStatus
});
self.add(task);
});
});
}
}
var userList = new List();
userList.load(url);
Upvotes: 0
Reputation: 338406
Try this:
function List(){
this.tasks = []; // prefer [] over new Array()
this.add = function(taskItem){
this.tasks.push(taskItem);
};
this.load = function(url){
var self = this;
$.getJSON(
url,
function (data){
$.each(data, function(key,val){
var task = new Task({
id:val.pkTaskId,
title:val.fldName,
status:val.fldStatus
});
self.add(task);
});
}
);
}
}
The issue is that this
is not what you think it is in the Ajax callback. The callback function is not called in the object's context, it is called in the global context (so this
will point to the window
object).
Saving an object reference (by convention called self
) beforehand is necessary.
this
will not always point to the object instance a function "belongs to". In fact, a function does not belong to an object in the same way it does in other languages. this
maintains the context a function is called in. Any function can be called in any context:
function A() {
this.val = "foo";
this.say = function () { alert( "A: " + this.val ); };
}
function B() {
this.val = "bar";
this.say = function () { alert( "B: " + this.val ); };
}
function test() { alert( "T: " + this.val ); }
var a = new A(), b = new B();
a.say() // alerts "A: foo"
b.say() // alerts "B: bar"
b.say.call(a); // alerts "B: foo"; (.call() switches the context)
test() // alerts "T: undefined" (val does not exist in window)
test.call(b) // alerts "T: bar" (Ah!)
Unless you define context implicitly (b.say()
implies that this
will be b
) or explicitly (by using call()
or apply()
), the context will be the global context - which in a browser is the window
object. And that's exactly the case for your Ajax callback.
Upvotes: 2
Reputation: 4466
Use this syntax:
function List() {
this.tasks = new Array();
}
List.prototype.add = function(taskItem) {
this.tasks.push(taskItem);
}
var list = new List();
list.add(…);
Also, try to improve your accept rate, people will be more willing to help you.
Upvotes: 0