Understanding closure and scope - javascript

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.

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) {
// ...
});

Passing in array of functions as parameter, for each func in functions run the function

I am trying to pass in an array of functions as a parameter to function x then execute them within the function x. I will also somehow pass in parameters but some of the parameters are only initialised within function x.
Some functions include things like:
_showData(data,type);
console.log(data);
$('#loading').remove();
Here is a sample:
// Called somewhere else
runFunctions([$('.dashboard').remove, $('.screen-loading').remove]);
var runFunctions = function(functions){
// do some things
for (var i = 0; i < functions.length; i++){
functions[i]();
}
Any ideas?
EDIT:
Sorry I just realised the program doesn't know what the objects are because I'm changing scope with an ajax call.
var runFunctions = function(functions){
$.ajax({
method: "POST",
url: "php/database.php",
dataType: "JSON",
data: {type:type},
success: function(data, type){
for (var i = 0; i < functions.length; i++){
functions[i]();
}
}
})
}
What about this:
_accessDatabase(
function(onSuccess){
$('.dashboard').remove();
var type = 'home';
_showData(data,type); // it doesn't know what data is, how can I pass it through?
$('.screen-loading').remove();
}
);
var _accessDatabase = function(onSuccess){
$.ajax({
method: "POST",
url: "php/database.php",
dataType: "JSON",
data: {},
success: function(data){
onSuccess(data);
}
})
}
I want to pass through var data to the onSuccess function, how can I do this?
Solved with:
var _request_successful = function onSuccess (data){
console.log("running onSuccess");
$('.dashboard').remove();
var type = 'home';
_showData(data,type);
$('.screen-loading').remove();
}
_accessDatabase(_request_successful);
var _accessDatabase = function(onSuccess){
$.ajax({
method: "POST",
url: "php/database.php",
dataType: "JSON",
data: {},
success: function(data){
onSuccess(data);
}
})
}
The problem with this code is that the functions that you're calling within the forLoop aren't bound to anything. Take this instead.
// Called somewhere else
runFunctions([
$('.dashboard').remove.bind($('.dashboard'))
, $('.screen-loading').remove.bind($('.screen-loading'))
]);
function runFunctions(functions){
// do some things
for (var i = 0; i < functions.length; i++){
console.log("running")
functions[i]();
}
}
What you could do instead is this:
function call(method, objs) {
objs.forEach(function (obj) {
obj[method]()
})
}
call('remove', [$('.dashboard'), $('.screen-loading')])
Here's a working fiddle: https://jsfiddle.net/ogfgocp4/
To explain a bit how it works, I don't know exactly the internal of JavaScript, but when you do: $('.dashboard').remove, it will return you the remove function. If you call it immediatly, it will be bound to the object which give you the method. If you affect it to something else, then it will be bound to the object it's being called from.
Here's a small snippet of code that explains it well I guess.
var obj = {
fun: function () {
console.log(this)
}
}
var fun2 = {
a: 1
}
//this -> obj
obj.fun()
// this -> window
fun = obj.fun
fun()
// this -> fun2
fun2.fun = obj.fun
fun2.fun()
When you call obj.fun, this will be the object obj. When you affect the method to a var, this then become window as it is the default object in this scope. Then if we finally bind the function to object fun2 and call it immediatly, this is now the object fun2.

How to create a object/function with defined setting when create new?

I wish to do something like this:
function Student (id, class) {
var id = id
var class = class
this.get = function (subject) {
$.ajax({
url: 'myurl',
data: { id: id, class: class, subject: subject },
success: function (r) { return r }
})
}
this.set = function (subject, mark) {
$.ajax({
url: 'myurl',
method: 'post',
data: { id: id, class: class, subject: subject, mark: mark },
success: function (r) { return r }
})
}
}
my question is how can I modify my function so that I can create new student as below
var s1 = new Student (22, 4) // to set predefined id & class
but, I want the set and get as below (like jquery set & get)
s1("math") // to get
s1("history", 70) // to set
**
so i think the answer is not possible to work as an object to store attribute id & class and call like a function without function name. thanks for your answer guys.
**
You can check how many arguments the caller has provided. Or check for undefined values.
function test(a, b) {
// both ifs check b was not provided
if (typeof b === "undefined") {
}
if (arguments.length == 1) {
}
}
Your current functions probably won't work because you are returning from a callback. AJAX is (in most cases) asynchronous. So in your case, you have to add another argument for providing a callback.
this.get = function (subject, callback) {
$.ajax({
url: 'myurl',
data: { id: id, class: class, subject: subject },
success: function (r) { callback(r); }
})
}
FYI, class is a reserved keyword by the ECMAScript specification.
function sample(x,y){
id=x;
subjectClass =y;
if(arguments.length == 1){
//getter
}
else{
//setter
}
}
call getter
sample("maths")
call setter
sample("history",70);
Note :
class is a reserved keyword, so please remove it and you can use some other variable name
but, I want the set and get as below
That would mean s1 would be a function, not a Student instance. So your constructor would need to return that.
function student(id, klass) {
// no need to declare variables here that are parameters already
return function(subject, mark) {
var data = {id: id, class: klass, subject: subject},
opt = {url: 'myurl', data: data};
if (arguments.length > 1) { // something was given to `mark`
data.mark = mark;
opt.method = "post";
}
return $.ajax(opt);
};
}
Btw, since you cannot return the response from an ajax call, the function will return the jqXHR promise:
var s1 = student(22, 4); // `new` is unnecessary now
s1("math").then(function(r) { console.log("got maths result:", r); });
s1("history", 70).then(function(r) { console.log("successfully set marks"); });

JavaScript/jQuery: Use 'this' Object on Success

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
});

Custom jQuery function - selector doesn't find element

I'm playing around with making a REST api and I'm working on some javascript functions.
The idea here is to run for example: $('#main').get('car/ford'); and the data returned will be added in the element provided.
Here is all the javascript:
$.fn.extend({
get: function (path) {
request(this, 'GET', path);
}
});
function request(element, type, path) {
var dees = $(element);
$.ajax({
type: type,
url: '/request/'+path,
success: function(data) {
console.log('Success');
a = $(element);
b = $('#fileList'); // this is a control
dees.html(data);
}
});
}
(function() {
console.log('running');
$('#fileList').get('car/ford');
})();
The problem I'm having is that when I run a.html(data);
Nothing will change. But if i run b.html(data);
Everything works like it should.
So there is a difference between those two selectors.
On a the element is not found a.length == 0
and on b the element is found b.length == 1
Why isn't the element found by the selector and how can I fix it?
The problem was solved by adding $ in front of the calling function.
From:
(function() {
console.log('running');
$('#fileList').get('car/ford');
})();
To:
$(function() {
console.log('running');
$('#fileList').get('car/ford');
});
You could try the following:
function request(element, type, path) {
var dees = $(element);
$.ajax({
type: type,
url: '/request/'+path,
success: function(data) {
console.log('Success');
dees.html(data);
}
});
}
in case the $(this) variable is conflicting with own $(this) variable of ajax() block.
Change element to $(element)
When request is call request(this, 'GET', path); this represents javascript object and it should be jQuery object. You need to pass jquery object or convert it to jquery object after being pass as I did.
$.fn.extend({
get: function (path) {
alert(this.tagName);
var objToPass = $(this);
request(objToPass, 'GET', path);
}
});
function request(javascriptObj, type, path) {
element = $(javascriptObj);
$.ajax({
type: type,
url: '/request/'+path,
success: function(data) {
console.log('Success');
a = $(element);
b = $('#fileList'); // this is a control
a.html(data);
}
});
}
Update
The call to get function should be instantiated on document.ready which could be done by simply adding $
Change
(function() {
console.log('running');
$('#fileList').get('car/ford');
})();
To
$(function() {
console.log('running');
$('#fileList').get('car/ford');
})();

Categories