How to pass a value to an AngularJS $http success callback - javascript

In my AngularJS application I am doing the following
$http.get('/plugin/' + key + '/js').success(function (data) {
if (data.length > 0) {
console.log(data);
// Here I would also need the value of 'key'
}
});
Now I need to access the key value within the success callback, i.e. I need to know which value it had when the get() request has been made.
Any "best practice" how to do so?
PS: I can do the following, but is there a better way?
var key = config.url.split('/')[2];

Solution 1:
$scope.key = key;
$http.get('/plugin/' + key + '/js').success(function (data) {
if (data.length > 0) {
console.log(data, $scope.key);
}
});
Solution 2 (Updated per Jim Hong's observation in his answer):
$http.get('/plugin/' + key + '/js').success((function(key) {
return function(data) {
console.log(key, data);
}
})(key));

Reference to #geniuscarrier
The working solution on my side is
$http.get('/plugin/' + key + '/js').success((function(key) {
return function(data) {
console.log(key, data);
}
})(key));
Since using #geniuscarrier, I'l get
data undefined error
.

Technically speaking, this is not an AngularJS problem but a feature of javascript
first of all, functions that you defined inside a scope will have access to local variable and parameter of its parent scope
function parent(arg){
var local
function child(){
// have access to arg and local
}
}
Scope actually works well with the parent-child analogy: if you are the parent and you own a cookie, of cause you are welling to share it with your children...but if you are a kid...your cookie is your cookie, your parent is not allowed to touch it :). In other words, inner scope can access outer scope but it does not work both ways
So you should definitely be able to do:
$http.get('/plugin/' + key + '/js').success(function (data) {
if (data.length > 0) {
console.log(data, key); //as long as you can pass it to $http.get as an argument
//you can access it here
}
});
Secondly, because of the event-driven nature of javascript, inner function store references to the outer function’s variables. you probably have heard of this
functions in javascript are objects
local variables and parameters are thus private members of the function:
function ObjectA(){ // define a constructor
var x = 10 // private variable
changeX : function(){
x = 20 // access and MODIFY a variable of parent scope
}
}
if you can understand how private variable works in javascript, then you basically understand what closure is. Thus, for call back function, it is very possible that by the time it is triggered, the value of the parent scope variable is already changed. To fix this, you can use an Immediately Invoked Function Expression (IIFE)
$http.get('/plugin/' + key + '/js').success((function(currentKeyValue) {
return function(data) {
console.log(currentKeyValue, data);
// again, currentKeyValue is a REFERENCE to outer function's
// parameter. However, since String is passed by value in javascript
// currentKeyValue of outer scope is a DIFFERENT string that has the
// same value as KEY when it is invoked
}
})(key)); // immediately invoke the function passing the key as a parameter

Instead of polluting scope or complicating with iif, another cleaner way is to create a callback function and call it with parameters;
var myCallBack = function (key) {
return function (data) {
if (data.length > 0) {
console.log(data, key);
}
}
}
$http.get('/plugin/' + key + '/js').success(myCallBack(key));

Phew, I was looking for this answer for so long, but it's good it is here. Just to update it, since legacy promise methods success and error have been deprecated and we should use the standard then method instead.
Solution 2 in #geniuscarrier and #jim-horng answers may be rewritten like this:
$http.get('/plugin/' + key + '/js').then(
(function(key) {
return function(data) {
console.log(key, data);
}
})(key),
function(data) {
//error handle
});

Related

Javascript global variable undeclared problem

In the mentioned sample code I'm trying to get a value for val variable. I declared it with global scope. And add some console log to verify values. But inside of the function it assign value without problem. But out of the function val is undeclared. why is that?
$.validator.addMethod("serialverify", function(){
var val;
$("#serialno").keyup(function(){
serial().done(function(data){
console.log('final = ' + data);
val = data;
console.log(val);
});
}); console.log(val);
return val;
}, "Please enter valid serial code");
There is a fundamental problem in your code. The assignment is taking place in a callback function, which means it'll only execute when the action keyup is made.
However, the statement return val is happening synchronously.
Suggestion -
You can wrap it in a promise of some sort and resolve it when done is executed. Something on the lines of -
......done( function(data) { ..... resolve(val); })
or declare var val; as global that is outside, the addMethod function

Why doesn't my object's property update in JavaScript?

I have a function, which I have prepared for a constructor call...
function Queue() {
if (!(this instanceof Queue)) return new Queue();
this.capacity = {};
this._count = 0;
}
And these method are being set on the the prototype property of Queue...Everything kosher right?
Queue.prototype.enqueue = function(name, options) {
this.capacity[name] = options || {};
this.count();
if (this._count > 5) {
return 'Max capacity has been reached—which is five, please dequeue....'
}
};
Queue.prototype.count = function() {
var total = Object.keys(this.capacity);
total.forEach(function(elem) {
this._count++
});
if (this._count == 1) {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There is one item in the queue');
} else {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There are ' + this._count + ' items in the queue');
}
};
My question how do i get this._count to increment when the enqueue/count method fires? I keep getting:
There are 0 items in the queue
I know I could add it on the .prototype property and place that in the count function and have it reference a local var...
Queue.prototype.count = function() {
var total = Object.keys(this.capacity), count = 0;
total.forEach(function(elem) {
this.count++
});
Queue.prototype.call = call // <-- weird no?
if (this.count == 1) {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There is one item in the queue');
} else {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There are ' + this.count + ' items in the queue');
}
};
But that seems not to be elegant...
Thanks in advance!
You need to bind this within forEach
Queue.prototype.count = function() {
var total = Object.keys(this.capacity);
total.forEach(function(elem) {
this._count++
}.bind(this)); //bind the context
if (this._count == 1) {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There is one item in the queue');
} else {
console.log(this.capacity[Object.keys(this.capacity)])
console.log( 'There are ' + this._count + ' items in the queue');
}
};
Try following modification (bind the function):
total.forEach(function(elem) {
this._count++
}.bind(this));
The problems is that this refers to a different object than in the parent function, because in JS, closures do not preserve this but instead the caller decides the this value. Alternatively, you can use the second thisArg argument of foreach.
The existing answers provide good solutions to the problem itself, I just thought I'd elaborate a bit more on the why.
this is a reference assigned by the execution context. More plainly it's a reference that's determined by the call site of the function. Since you can pass functions around in JavaScript like any other value this can lead to problems being caused by that reference being a moving target.
The issue with your code is that you're referring to this inside of a forEach. forEach takes a function as an argument and calls it, since what this is pointing to is determined by where the function is called and not where it's defined the value is something different when it gets called. It ends up falling back to whatever global context or undefined if you're in strict mode.
There are a number of different ways to handle the problem.
You could store a reference to the outer this on a variable and use it inside the other function.
var self = this;
total.forEach(function(elem) {
self._count++;
});
You could use .bind. It's a function method which returns a function that uses the passed in object as the reference for this no matter where you call it.
total.forEach(function(elem) {
this._count++;
}.bind(this));
Or you could use an arrow function. Arrow functions don't create their own context so they'll just maintain the value of this from the surrounding one.
total.forEach(() => {
this._count++;
});
This is a common problem and these are all valid solutions. They go from least to most elegant in my opinion.

Javascript Function Scoped For Loops

Here's an example of a situation where a simple JS loop does not behave as expected, because of the loop variable not being in a separate scope.
The solution often presented is to construct an unpleasant-looking bit of loop code that looks like this:
for (var i in obj) {
(function() {
... obj[i] ...
// this new shadowed i here is now no longer getting changed by for loop
})(i);
}
My question is, could this be improved upon? Could I use this:
Object.prototype.each = function (f) {
for (var i in this) {
f(i,this[i]);
}
};
// leading to this somewhat more straightforward invocation
obj.each(
function(i,v) {
... v ...
// alternatively, v is identical to
... obj[i] ...
}
);
when I ascertain that I need a "scoped loop"? It is somewhat cleaner looking and should have similar performance to the regular for-loop (since it uses it the same way).
Update: It seems that doing things with Object.prototype is a huge no-no because it breaks pretty much everything.
Here is a less intrusive implementation:
function each (obj,f) {
for (var i in obj) {
f(i,obj[i]);
}
}
The invocation changes very slightly to
each(obj,
function(i,v) {
... v ...
}
);
So I guess I've answered my own question, if jQuery does it this way, can't really go wrong. Any issues I've overlooked though would warrant an answer.
Your answer pretty much covers it, but I think a change in your original loop is worth noting as it makes it reasonable to use a normal for loop when the each() function isn't handy, for whatever reason.
Update: Changed to use an example that's similar to the example referenced by the question to compare the different approaches. The example had to be adjusted because the each() function requires a populated array to iterate over.
Assuming the following setup:
var vals = ['a', 'b', 'c', 'd'],
max = vals.length,
closures = [],
i;
Using the example from the question, the original loop ends up creating 2n functions (where n is the number of iterations) because two functions are created during each iteration:
for (i = 0; i < max; i++) {
closures[i] = (function(idx, val) { // 1st - factoryFn - captures the values as arguments
return function() { // 2nd - alertFn - uses the arguments instead
alert(idx + ' -> ' + val); // of the variables
};
})(i, vals[i]);
}
This can be reduced to creating only n + 1 functions by creating the factory function once, before the loop is started, and then reusing it:
var factoryFn = function(idx, val) {
return function() {
alert(idx + ' -> ' + val);
};
};
for (i = 0; i < max; i++) {
closures[i] = factoryFn(i, vals[i]);
}
This is nearly equivalent to how the each() function might be used in this situation, which would also result in a total of n + 1 functions created. The factory function is created once and passed immediately as an argument to each().
each(vals, function(idx, val) {
closures[idx] = function() {
alert(idx + ' -> ' + val);
};
});
FWIW, I think a benefit to using each() is the code is a bit shorter and creating the factory function right as it's passed into the each() function clearly illustrates this is its only use. A benefit of the for loop version, IMO, is the code that does the loop is right there so it's nature and behavior is completely transparent while the each() function might be defined in a different file, written by someone else, etc.
Global Scope
When something is global means that it is accessible from anywhere in your code. Take this for example:
var monkey = "Gorilla";
function greetVisitor () {
return alert("Hello dear blog reader!");
}
If that code was being run in a web browser, the function scope would be window, thus making it
available to everything running in that web browser window.
Local Scope
As opposed to the global scope, the local scope is when something is just defined and accessible in a
certain part of the code, like a function. For instance;
function talkDirty () {
var saying = "Oh, you little VB lover, you";
return alert(saying);
}
alert(saying); // Throws an error
If you take a look at the code above, the variable saying is only available within the talkDirty
function. Outside of it it isn’t defined at all. Note of caution: if you were to declare saying without
the var keyword preceding it, it would automatically become a global variable.
What this also means is that if you have nested functions, the inner function will have access to the
containing functions variables and functions:
function saveName (firstName) {
function capitalizeName () {
return firstName.toUpperCase();
}
var capitalized = capitalizeName();
return capitalized;
}
alert(saveName("Robert")); // Returns "ROBERT"
As you just saw, the inner function capitalizeName didn’t need any parameter sent in, but had complete
access to the parameter firstName in the outer saveName function. For clarity, let’s take another
example:
function siblings () {
var siblings = ["John", "Liza", "Peter"];
function siblingCount () {
var siblingsLength = siblings.length;
return siblingsLength;
}
function joinSiblingNames () {
return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n");
}
return joinSiblingNames();
}
alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter"
As you just saw, both inner functions have access to the siblings array in the containing function, and
each inner function have access to the other inner functions on the same level (in this case,
joinSiblingNames can access siblingCount). However, the variable siblingsLength in the siblingCount is
only available within that function, i.e. that scope.

Why does this JavaScript function always returns 'undefined' – and how to fix that?

I'm using phonegap with application preferences plugin and trying to make a helper function to get a value from it. However the function is not returning a correct value. I know this has to do with asynchronous thingy but unfortunately I don't know how to fix it. (I've tried to search help here, and found little, and tried to implement it in helper method)
What I want to achieve is:
function populateList() {
var a = 1;
var number = getSettingFromApplicationPreferences('number');
// number is always undefined
var letter = getSettingFromApplicationPreferences('letter');
// letter is always undefined
number = (number) ? number : 1;
letter = (letter) ? letter : 'b';
// Here I'll do some DOM manipulation and use 'number' and 'letter' on it, and
// define new variables based on 'number' and 'letter'
}
here's the helper function that I need help with:
function getSettingFromApplicationPreferences(setting) {
var x = (function () {
window.plugins.applicationPreferences.get(
// setting
setting,
// success callback
function(returnValue) {
console.log(setting + ': ' + returnValue);
return returnValue;
},
// error callback
function(error) {
alert("Failed to get a setting: " + error);
return false;
}
);
})();
return x;
}
Question
How is it possible to return the 'returnValue' from application preferences with that helper function?
The problem is, your callback doesn't actually set a value for x. So, you're going to have some other way to do whatever you're doing, because return values will not work.
You are using an asynchronous function incorrectly, you cannot assign like you are because the function hasn't returned yet and so you get an undefined. You need to use a callback function instead.
That means that inside the success function you would do whatever you need to do with the "returnValue".

Javascript callback functions execution

I'm not sure the correct term for this. But I want to write a function that accepts another function and execute it.
For eg.
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc.call(newData);
}
test("hello", function(data){
alert(data);
});
Data is supposed to contain "hello Shawn" string. Help me rewrite this the correct way please.
The first argument of the call method is used to set the this keyword (the function context) explicitly, inside the invoked function, e.g.:
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc.call(newData);
}
test("hello", function () {
alert(this); // hello Shawn
});
If you want to invoke a function without caring about the context (the this keyword), you can invoke it directly without call:
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc(newData); // or aFunc.call(null, newData);
}
test("hello", function (data) {
alert(data);
});
Note that if you simply invoke a function like aFunc(newData); or you use the call or apply methods with the this argument set as null or undefined, the this keyword inside the invoked function will refer to the Global object (window).
Looks fine but you can just change
aFunc.call(newData);
to
aFunc(newData);
You were close. The first argument to "call" is the "scope" argument. In this case, it doesn't matter what it is, because you're not using "this" anywhere in your anonymous function, so any value will suffice.
function test(data, aFunc) {
var newData = data + " Shawn";
aFunc.call(this, newData);
}
test("hello", function(data){
alert(data);
});

Categories