JavaScript/jQuery: Use 'this' Object on Success - javascript

I have a somewhat annoying issue when it comes to sending an Ajax request to a server and then returning the data on the success function within an JavaScript object. I've searched for similar questions, but none were really of the same manner as mine.
For example, I have the following code for sending a request within an object:
function SomeObject ( someVar )
{
var someVar = someVar;
}
SomeObject.prototype.sendRequest = function ()
{
$.ajax(
{
url : "somePage.php",
type : "POST",
data :
{
someVar : someVar
},
success : this.parseSuccess
} );
};
SomeObject.prototype.parseSuccess = function ( data )
{
if ( data === "success" )
{
this.proceed(); // Error
}
else
{
alert( "Server failed request." );
this.proceed();
}
};
SomeObject.prototype.proceed = function ()
{
// Do something else
};
I know that this.proceed() will fail because this is not the SomeObject instance.
Still, how can I efficiently refer back to the object after the request is complete?
I found I could do the following to achieve what I want, but it does not feel proper, and I would like a better way to handle the Ajax calls:
SomeObject.prototype.sendRequest = function ()
{
var me = this;
$.ajax(
{
url : "somePage.php",
type : "POST",
data :
{
someVar : someVar
},
success : function ( data )
{
me.parseSuccess( data ); // Will work
}
} );
};
Thanks for any help on the matter.

You could always do this:
success : function ( data )
{
parseSuccess.apply(this, [data]); // or parseSuccess.call(this, data);
}
Basically, with apply(), you can pass in the context of this you want to use inside the function, which in this case is the SomeObject context.

You can use the context option of $.ajax. Specifically, using context: this will set the this value inside parseSuccess to the current this value, which is what you want.
$.ajax({
// ...
context: this,
success: this.parseSuccess
});

Related

Accessing variable in .filter() scope with Mixpanel JQL

Working with Mixpanel & JQL I am trying to access a variable from the global scope in the .filter() function, so I could query only the desired records:
var selectedVal = 'foo';
MP.api.jql(function main() {
return People()
.filter(function(user) {
// Compare with 'selectedVal'
return user.properties["user-title"] == selectedVal;
})
;
}, selectedVal).done(function(results) {
// ...
});
Error:
{"request": "/api/2.0/jql/", "error": "Uncaught exception ReferenceError: selectedVal is not defined\n return user.properties[\"user-title\"] == selectedVal;\n ^\n\nStack trace:\nReferenceError: selectedVal is not defined\n at :6:70\n"}
If anyone could point me in the right direction, that would be great. Thank you
Edit:
At the moment I'm using a workaround by fetching all People entities and filtering afterwards. This is not optimal at all and thus am still looking for a way to get the result set on a property condition of the People entity.
Use bind. You can declare the function outside of MP.api.jql block and inside that jql function, you would pass in as a parameter, main.bind(null,whatever).
Looking into the unminified mixpanel-platform JS, the jql() function accepts a params object as second argument:
...
jql: function(script, params, settings) {
params = params || {};
settings = _.extend({type: 'POST'}, settings);
return this.query('/api/2.0/jql/', {
script: String(script),
params: JSON.stringify(params)
}, settings, function(data) {
return JSON.parse(data);
});
},
...
Solution:
var params = {
selectedVal : 'foo'
};
MP.api.jql(function main() {
return People()
.filter(function(user) {
return user.properties["user-title"] == params.selectedVal;
})
;
}, params).done(function(results) {
// ...
});

Method undefined when calling it from $ajax.success

When I call a method from the $ajax.success callback I get an undefined.
var someObj = {};
someObj.someMethod = function() {
//code code
}
someObj.ajaxCall = function() {
$.ajax({
//ajax options
})
.done(function( data ) {
this.someMethod();
});
}
As our good friend, Mr. SLaks has pointed out, you have a scope issue with regards to this
One solution other than the one posted could be saving a reference to the scope before the callback :
someObj.ajaxCall = function() {
var _this = this;
$.ajax({
//ajax options
})
.done(function( data ) {
_this.someMethod();
});
}
Or, you can use the context option with $.ajax() to control the setting of the this value:
someObj.ajaxCall = function() {
$.ajax({
context: this,
// other ajax options
})
.done(function( data ) {
this.someMethod();
});
}
You should use the call method of the function object:
someObj.ajaxCall = function() {
$.ajax({
//ajax options
})
.done(function( data ) {
someMethod.call(someObj);
});
}
Inside the success callback the this object is pointing the $ajax object which haven't a someMethod function defined.

jQuery callback and prototype inheritance

I create the following class :
APP.core.View = function () {
var self = this;
$.ajax ( { url: 'test.html' } ).done ( self.build );
return self;
};
APP.core.View.prototype.build = function ( source ) {
var self = this;
// this refers to the AJAX callback.
return self;
};
As you can see in the build method, the reference to this (the one belonging to APP.core.View) has been lost. How can I get it back ? I know I could pass a ref to this in the AJAX callback like this :
$.ajax ( { url: 'test.html' } ).done ( function ( source ) {
self.build ( source, self );
} );
But I don't really like it as I feel like a method should never loose the ref to its object.
Any idea/suggestion ? :)
You can use $.proxy() to create a cross platform solution
APP.core.View = function () {
$.ajax({
url: 'test.html'
}).done($.proxy(this.build, this));
return this;
};
For modern browsers, you can use .bind()
APP.core.View = function () {
$.ajax({
url: 'test.html'
}).done(this.build.bind(this));
return this;
};
I just found another answer in the jQuery AJAX doc. The jQuery.ajax function provides a context argument which lets you specifies the callbacks context. Example :
$.ajax({
url: "test.html",
context: document.body
}).done(function() {
$( this ).addClass( "done" );
});
Source : http://api.jquery.com/jQuery.ajax/

pass this local js variable as an argument to a new function

I've read several posts on passing local function variables to new functions in javascript but am still having trouble in my own particular case.
I'm trying to pass the term argument given to data: function (term, page) to the generateUrl function below it. this.term = term and window.term = term (which I know is bad practice) aren't working. Should I try to declare a term variable outside of $(document).ready(function) or outside of the two inner functions, or or should I move the generateUrl definition inside of the $("#example").select2 function?
$(document).ready(function(){
$("#example").select2({
ajax: {
url: generateUrl(),
data: function (term, page) {
this.term = term; // i want to pass this local variable to generateUrl
}
}
});
function generateUrl(term) {
(function ($) {
var args = 'keywords=' + term;
return args;
}
(jQuery));
}
});
$(document).ready(function(){
$("#example").select2({
ajax: {
url: generateUrl(), /* You are calling generateUrl without parameter,
this will cause error
Did you actualy mean generateUrl(term) ? */
data: function (term, page) {
this.term = term; // i want to pass this local variable to generateUrl
generateUrl( this.term ); /* Is this what you want to do? */
}
}
});
function generateUrl(term) {
(function ($) {
var args = 'keywords=' + term;
return args;
}
(jQuery));
}
});
You should take a look into the select2 documentation, the example on section "Loading Remote Data" seems to be exactly what you're looking for. Based on that, I believe your code should be:
$(document).ready(function(){
$("#example").select2({
ajax: {
url: "", // replace that empty string with your ajax base url
// (without any parameters)
data: function (term, page) {
return { keywords : term };
}
}
});
});

Understanding closure and scope

For some reason (probably because i don't understand closures) function inResult always returns false and the loop is never executed. Of course i'm sure that result contains has the right properties.
function hasId() {return $(this).prop('id');}
function inResult(res) { return res.hasOwnProperty($(this).prop('id'));}
$.ajax({
url : opt.url,
data : $.extend(true, opt.data, {ids: ids}),
context : this, // A collection of elements
type : 'POST',
dataType : 'json',
success : function(result) {
// Filter elements with id and with a property in result named "id"
this.filter(hasId).filter(inResult(result)).each(function() {
console.log($(this).prop('id'));
});
}
});
EDIT: working code solution (thanks to Šime Vidas for poiting me in the right direction):
// Use closures to change the context later
var hasId = function() { return $(this).prop('id'); };
var inResult = function(res) { return res.hasOwnProperty($(this).prop('id')); };
$.ajax({
url : opt.url,
data : $.extend(true, opt.data, {ids: ids}),
context : this, // A collection of elements
type : 'POST',
dataType : 'json',
success : function(result) {
// Filter elements with id and with a property in result named "id"
var filtered = this.filter(function() {
// Note the context switch and result parameter passing
return hasId.call(this) && isBinded.call(this, result);
});
filtered.each(function() { console.log($(this).prop('id')); });
}
});
Try this:
this.filter( hasId ).filter( function () {
return inResult( result );
}).each( function () {
console.log( this.id );
});
In your code you have .filter(inResult(result)) which won't work because you're invoking inResult immediately and passing the result of that invocation (which is a Boolean value) to filter(), which doesn't work with Boolean values.
You could also do it like so:
var keys = Object.keys( result );
var filtered = this.filter( function () {
return this.id && keys.indexOf( this.id ) > -1;
});
Object.keys( result ) returns an array of all own property names from result.

Categories