$http.get("./data/web.json") request succesfully and return a array. When I loop the array by doing request, the iterator variable i would be undefined?! So how can I access the return array and doing the loop request?
<script>
var ngApp = angular.module("webApp", ['xml'])
.config(function ($httpProvider) {
$httpProvider.interceptors.push('xmlHttpInterceptor');
})
.controller("webCtrl", function($scope, $http) {
$http.get("./data/web.json")
.success(function(response) {
$scope.websites = response;
for (i = 0; i < $scope.websites.length; i++){
$http.get('../api/alexa?url=' + $scope.websites[i].url)
.success(function(rsp) {
//$scope.websites[i].rank = rsp.data.POPULARITY.TEXT;
console.log($scope.websites[i]);
});
}
console.log(response);
});
});
</script>
.controller("webCtrl", function ($scope, $http) {
$http.get("./data/web.json")
.success(function (response) {
$scope.websites = response;
for (i = 0; i < $scope.websites.length; i++) {
$scope.processWebsites($scope.websites[i], i);
}
});
$scope.processWebsites = function (website, index) {
$http.get('../api/alexa?url=' + website.url)
.success(function (rsp) {
console.log($scope.websites[index]);
});
}
});
Try this code. This will create a new execution context, thereby removing any unintentional side effects due to async execution.
You want to access i variable but if your request taking to much time then for
loop will not wait it will do ajax call and execute so after for loop
end your i will be $scope.websites.length + 1 (because of i++) so you will get undefined to solve this problem you have to use closure function
JavaScript closure inside loops – simple practical example
var funcs = [];
function createfunc(i) {
return function() {
$http.get('../api/alexa?url=' + $scope.websites[i].url)
.success(function(rsp) {
//$scope.websites[i].rank = rsp.data.POPULARITY.TEXT;
console.log($scope.websites[i]);
});
};
}
$http.get("./data/web.json")
.success(function(response) {
$scope.websites = response;
for (i = 0; i < $scope.websites.length; i++) {
funcs[i] = createfunc(i)
$http.get('../api/alexa?url=' + $scope.websites[i].url)
.success(function(rsp) {
//$scope.websites[i].rank = rsp.data.POPULARITY.TEXT;
});
}
console.log(response);
});
for (i = 0; i < funcs.length; i++) {
funcs[i]();
}
I am not sure How your response json looks like but it should be array of key value pairs or a single key value pair
so if you have like
[ {key:value},{key:value},{key:value}]
as response assuming a key is url in your case
it should directly work for you now you are asking for a key called i
that is website[i] which is undefined.
try doing this
foreach loop
i isn't defined at your second success callback since it is an async callback and when it is been called the parent scope isn't valid anymore since it was executed without defining any local variables, you should define the iterator inside the for loop so it will be proper declared and hoisted
but you should be aware that since it is an async callback you will have a race condition when most of the chances that the loop will end before the first call back will be called and the iterator value in all of the iteration will be the array size (the last value)
var a = [1, 2, 4]
for (var i = 0; i < a.length; i++) {
var that = this;
that.iterator = i;
setTimeout(function() {
alert(a[that.iterator]);
}, 10);
}
I would suggest you to aggregate the calls and use an aggregated callback to handle all of them together using $q.all
Related
I have a datatable, a checkbox on each table, and a button that will trigger my operation on that row. I would like to remove that row when my operation is done.
for (i = 0; i < checkedBoxes.length; i++) {
var chk = checkedBoxes[i];
var tdef = chk.closest("tr").querySelectorAll('td');
var myThing = tdef[1].innerHTML;
service.doSomething(myThing, function (result) {
service.doSomethingElse();
// I would like to remove this row once I'm done with this row
//browseDataTable.row($(chk).parents('tr')).remove().draw();
});
}
I know that I'm not supposed to remove that row as I'm looping through it. So I'm planning to just collect the index of each row, and when everything is finished, I can remove it, like this:
var myArr = new Array();
for (i = 0; i < checkedBoxes.length; i++) {
service.doSomething(myThing, function (result) {
service.doSomethingElse();
myArr.push(i);
}) // Chrome said 'then' is undefined, so how do I chain callback here?
.then(function () {
// Remove all rows at index in myArr
});
}
The service isn't async service, it's an ASMX service.
You are using your service both like a function with a callback and a Promise. So which is it? Does it take a callback, or does it return a Promise?
It looks like it does not return a Promise, because you are trying to chain .then() and it's undefined.
The service isn't async then why are you giving it a callback and trying to chain a .then(), if it's synchronous?
Anyway, one easy way to solve your issue is to use let, which will create a scope for every loop.
Currently :
for (i = 0; i < checkedBoxes.length; i++) { // i is a global (window) variable, that's bad
service.doSomething(myThing, function (result) {
service.doSomethingElse();
myArr.push(i); // i will always be checkboxes.length
})
}
By using let :
for (let i = 0; i < checkedBoxes.length; i++) { // i is in the local scope
service.doSomething(myThing, function (result) {
service.doSomethingElse();
myArr.push(i); // the value of i will be different (correct) each time
})
}
I have an angular view that has a table of rows consisting of a select list and an text box. When a select list index is changed, I need to update the corresponding text box on the same row with a lookup value from the database. I am using ng-Change on the select list to call a $scope function that utilizes $http.get to make the call through an ActionMethod. I have tried this in a million ways, and finally was able to extract a value from the $http.get function by assigning it to a scope variable, but I only ever get the value of the previous lookup triggered by the selected index change, not the current one. How can I get a value real-time? I understand it is asynchronous, so I know the nature of the problem. How do I work around it? Current state of my .js:
$scope.EntityId = null;
$scope.EntityNameChanged = function (item, block) {
for (var i = 0; i < block.length; i++)
{
if (item.Value == block[i].Name.Value) {
$scope.GetEntityId(item.Value);
block[i].Id = $scope.EntityId;
}
}
}
$scope.GetEntityId = function(name) {
$http.get("EntityId", { params: { EntityName: name } }).then(function success(response) {
$scope.EntityId = response.data[0].Value;
});
};
The GetEntityID function should return a promise
function GetEntityId(name) {
//save httpPromise
var p = $http.get("EntityId", { params: { EntityName: name } });
//return derived promise
return p.then(function onSuccess(response) {
//return chained data
return response.data[0].Value;
});
};
Then use an IIFE in the for loop.
$scope.EntityNameChanged = function (item, block) {
for (var i = 0; i < block.length; i++) {
//USE IIFE to hold value of i
(function IIFE(i) {
if (item.Value == block[i].Name.Value) {
//save promise
var p = GetEntityId(item.Value);
//extract value from promise
p.then(function onSuccess(Value) {
block[i].Id = Value;
});
}
})(i);
}
}
Because the onSuccess function gets invoked asynchronously after the for loop completes, an IIFE closure is necessary to preserve the value of i until after the data is returned from the server.
Your GetEntityId function is not async, even though it makes an async request. by the time it sets $scope.EntityId, the for loop has already exited.
You can't actually queue up async calls like this, because each one of them is trying to share a value outside the loop that could be set by any other iteration, so one item in the loop might get another item's return value.
Instead, you should return the promise back to the loop, and perform your .then in the loop. Something like the following:
$scope.EntityNameChanged = function(item, block) {
for (var i = 0; i < block.length; i++) {
if (item.Value == block[i].Name.Value) {
$scope.GetEntityId(item.Value)
.then(function success(response) {
block[i].Id = response.data[0].Value;
});
}
}
}
$scope.GetEntityId = function(name) {
return $http.get("EntityId", {
params: {
EntityName: name
}
});
};
(note this is untested, but should do what you expect).
To prompt Angular to update the value of the scope on its $watch loop, call $scope.apply() after assignment. This should bring you to the most recent value.
EDIT: This answer was wrong. It was a $http get request, which already uses $apply.
What you need to do is put the request inside a factory and return a promise. Require the factory in your controller.
app.factory('getData', function ($http, $q){
this.getlist = function(){
return $http.get('mylink', options)
.then(function(response) {
return response.data.itemsToReturn;
});
}
return this;
});
app.controller('myCtrl', function ($scope, getData){
app.getData()
.then(function(bar){
$scope.foo = bar;
});
});
I'm trying to count the number of elements returned from $resouce.query() object, so that I can increment it and use it as the ID for the next object to be saved.
Following is a service to communicate with the server:
eventsApp.factory('EventData', function($resource) {
var resource = $resource('/data/event/:id', {id: '#id'});
return {
getEvent: function(eventId) {
return resource.get({id: eventId});
},
saveEvent: function(event) {
var count = 0;
resource.query(function(data) {
count = data.length; // Accessible here!
});
event.id = count; // Not accessible here!
return resource.save(event);
},
getAllEvents: function() {
var count = 0;
var lol = resource.query(function(data) {
count = data.length;
});
console.log(count);
return resource.query();
}
}
});
However, as mentioned in the comments, I'm unable to access the length property. Any solutions?
By looking at your code what I am getting is your resource.query executes the callback function asynchronously because of that your event.id = count; is executed first and then the callback it executed. If you want to access data.length then you can use $q and create a defer. And then resolve that deferred object.
what shall I do to make the last row of code return a value?
$scope.runActionwithObjects = function() {
for (var i = 0; i < $scope.Objects.length; i++) {
console.log($scope.Objects[i]); //$scope is accessible
$http.get($scope.Objects[i]["Commit"]).success(function (data) {
console.log($scope.Objects[i]);//return undefined
The problem is due to asynchrony of ajax requests.
When the success callback is executed, your loop has already finished and the i variable is already equal to $scope.Objects.length.
Try forEach. This function will create different closures for items in the array.
$scope.Objects.forEach(function(currentObject){
console.log(currentObject); //$scope is accessible
$http.get(currentObject["Commit"]).success(function (data) {
console.log(currentObject);
});
});
The reason your $scope.Objects[i] is undefined because the variable i is always = $scope.Objects.lenth + 1, for example you got 5 elements, the i will be 6, because the at the time of callback, it already got the last value.
One solution is to bind needed object to that method, so we can access it via this(we can not reference directly by closure to ref variable, because it's still stored the last item), for example:
for (var i = 0; i < $scope.Objects.length; i++) {
var ref = $scope.Objects[i];
// console.log($scope.Objects[i]); //$scope is accessible
var successCallback = (function (data) {
console.log(this);//return the ref
}).bind(ref);
$http.get('').success(successCallback);
}
}
How do you go about knowing when a For Loop is done iterating and attach a callback.
This is a sample loop of many loops within a function that inserts records into indexedDB.
if (Object.hasOwnProperty("Books")) {
for (var i = 0, j = Object["Books"].length; i < j; i++) {
server.Book.add({
title: Object["Books"][i].Cat,
content: Object["Books"][i]
});
}
}
I need to be able to know when each of the if statements loops are finished then attach a callback. All the loops are being fired asynchronously, and I need to run a function_final() just when all loops are finished not when they are fired.
EDIT
What I have tried so far :
InsertDB = {
addBook: function(Object) {
return $.Deferred(function() {
var self = this;
setTimeout(function() {
if (Object.hasOwnProperty("Book")) {
for (var i = 0, j = Object["Book"].length; i < j; i++) {
server.Book.add({
title: Object["Book"][i].id,
content: Object["Book"][i]
});
}
self.resolve();
}
}, 200);
});
},
addMagaz: function(Object) {
return $.Deferred(function() {
var self = this;
setTimeout(function() {
if (Object.hasOwnProperty("Magaz")) {
for (var i = 0, j = Object["Magaz"].length; i < j; i++) {
server.Magaz.add({
content: Object["Magaz"][i]
});
}
self.resolve();
}
}, 2000);
});
},
addHgh: function(Object) {
return $.Deferred(function() {
var self = this;
setTimeout(function() {
if (Object.hasOwnProperty("MYTVhighlights")) {
for (var i = 0, j = Object["MYTVhighlights"].length; i < j; i++) {
server.MYTVhighlights.add({
content: Object["MYTVhighlights"][i]
});
}
self.resolve();
}
}, 200);
});
}, ect...
then on a AJAX success callback :
success: function(data){
var Object = $.parseJSON(data);
$.when(InsertDB.addBook(Object),
InsertDB.addMagaz(Object),
InsertDB.addUser(Object),
InsertDB.addArticles(Object),
InsertDB.addHgh(Object),
InsertDB.addSomeC(Object),
InsertDB.addOtherC(Object)).done(final_func);
function final_func() {
window.location = 'page.html';
}
Here final_func is fired before looping ends..
Thanks
You can use JavaScript closures, just like this:
if (Object.hasOwnProperty("Books")) {
for (var i = 0, j = Object["Books"].length; i < j; i++) {
(function(currentBook)
server.Book.add({
title: currentBook.Cat,
content: currentBook
});
)(Object["Books"][i]);
}
function_final();
}
For more information about closures you can refer here.
Since you've said that server.Book.add() is asynchronous, you will need a method of knowing when that asynchronous operation is completed and you can then use that to build a system for knowing when all of them are done. So, the pivotal question (which I already asked as a comment earlier and you have not responded to) is how you can know when server.Book.add() is actually complete. If you're using an indexedDB, then somewhere inside that function, there is probably a request object that has an onsuccess and onerror methods that will tell you when that specific operation is done and that information needs to get surfaced out to server.Book.add() in some way, either as a completion callback or as a returned promise (those two options are how $.ajax() operates for it's asynchronous behavior.
Let's suppose that server.Book.add() returns a promise object that is resolved or rejected when the asychronous .add() operation is complete. If that was the case, then you could monitor the completion of all the operations in your loop like this:
if (obj.hasOwnProperty("Books")) {
var promises = [], p;
for (var i = 0, j = obj["Books"].length; i < j; i++) {
p = server.Book.add({
title: obj["Books"][i].Cat,
content: obj["Books"][i]
});
promises.push(p);
}
$.when.apply($, promises).done(function() {
// this is executed when all the promises returned by
// server.Book.add() have been resolved (e.g. completed)
}).error(function() {
// this is executed if any of the server.Book.add() calls
// had an error
});
}
Let's suppose that instead of server.Book.add() returning a promise, it has a couple callbacks for success and error conditions. Then, we could write the code like this:
if (obj.hasOwnProperty("Books")) {
var promises = [], p;
for (var i = 0, j = obj["Books"].length; i < j; i++) {
(function() {
var d = $.Deferred();
server.Book.add({
title: obj["Books"][i].Cat,
content: obj["Books"][i],
success: function() {
var args = Array.prototype.slice.call(arguments, 0);
d.resolve.apply(d, args);
},
error: function() {
var args = Array.prototype.slice.call(arguments, 0);
d.reject.apply(d, args);
}
});
promises.push(d.promise());
})();
}
$.when.apply($, promises).done(function() {
// this is executed when all the promises returned by
// server.Book.add() have been resolved (e.g. completed)
}).error(function() {
// this is executed if any of the server.Book.add() calls
// had an error
});
}
So, since you've not disclosed how server.Book.add() actually indicates its own completion, I can't say that either of these blocks of code work as is. This is meant to demonstrate how you solve this problem once you know how server.Book.add() communicates when it is complete.
Promises/Deferreds are not magic in any way. They don't know when an operation is completed unless that operation calls .resolve() or .reject() on a promise. So, in order to use promises, your async operations have to participate in using promises or you have to shim in a promise to an ordinary completion callback (as I've done in the second code block).
FYI, I also change your Object variable to obj because defining a variable named Object that conflicts with the built-in Object in the javascript language is a bad practice.
use jquery when functionality
$.when( function1 , function2 )
.then( myFunc, myFailure );
I'd write something like this in pure JS, consider it as pseudo code:
var totalForLoopsCount = 3; //Predict for loops count here
var forLoopsFinished = 0;
function finalFunction()
{
alert('All done!');
}
function forLoopFinished()
{
forLoopsFinished++;
if(forLoopsFinished == totalForLoopsCount)
{
finalFunction();
}
}
var length = 10; //The length of your array which you're iterating trough
for(var i=0;i<length;i++)
{
//Do anything
if(i == length-1)
{
forLoopFinished();
}
}
for(var i=0;i<length;i++)
{
//Do anything
if(i == length-1)
{
forLoopFinished();
}
}
for(var i=0;i<length;i++)
{
//Do anything
if(i == length-1)
{
forLoopFinished();
}
}