Key Inside For Loop Is Same inside then function - angularjs

$scope.expandPackage = function (node, expanded) {
$scope.expansion=expanded;
var promises = [];
//Check that expand command was clicked and the node's children were not already fetched
if (expanded && !node.childrenFetched) {
//console.log("Call to fetch children");
//node.children = fetchChildren(node.name);
var promise=getPackageHierarchyByPackageId(node.packageId).then(function(){
if( $scope.packageEnabled) {
var featureinstancePromise=FeatureInstanceService.getfeatureInstanceForOrgUnitId(myRow.orgUnitId);
featureinstancePromise.then(function (featureInstance) {
featureInstanceList=featureInstance;
}).then(function(){
prepareListOfFeatureInstance(featureInstanceList,featureList);
for (var key in featureAndFeatInstanceIdMap) {
if (featureAndFeatInstanceIdMap.hasOwnProperty(key)) {
console.log(key + " -> " + featureAndFeatInstanceIdMap[key]);
var enablementPromise=FeatureInstanceService.getEnablementInfo(key);
promises.push(enablementPromise);
enablementPromise.then(function(enablementInfoSuccessResponse){
$scope.featureAndEnablementInfo[featureAndFeatInstanceIdMap[key].displayName]=enablementInfoSuccessResponse;
});
}
}
$q.all(promises).then(function(){
$scope.featureList=featureList;
});
});
}
});
node.childrenFetched = true;
}
};
this.getEnablementInfo = function(featureInstanceId) {
var defer = $q.defer();
$http.get(baseUrl + 'featureInstances/'+featureInstanceId +'/enablementInfo') .
success(function(data, status, headers, config) {
defer.resolve(data);
}).error(function(data, status, headers, config) {
console.log("Error message: " + data.message);
defer.reject(data);
});
return defer.promise;
};
while executing then function it always return same key but the enablementInfoSuccessResponse is fine. My question is how would I know which response is for which key ? Why key is always same ? How I can get rid out of this problem ?

The key is always the same because it is scoped to the then function called after resolving featureinstancePromise. Since promises exist for handling asynchronous code, the for loop keeps on iterating while not caring about when the promise will resolve. This is great but you cannot rely on key being the same since it is updated on every loop.
There are several ways to solve this but the easiest is to move the logic in your loop into a function and pass in the key. That way key will be scoped to that function and won't change underneath you.
function loopWork(key) {
if (featureAndFeatInstanceIdMap.hasOwnProperty(key)) {
console.log(key + " -> " + featureAndFeatInstanceIdMap[key]);
var enablementPromise=FeatureInstanceService.getEnablementInfo(key);
promises.push(enablementPromise);
enablementPromise.then(function(enablementInfoSuccessResponse){
$scope.featureAndEnablementInfo[featureAndFeatInstanceIdMap[key].displayName]=enablementInfoSuccessResponse;
});
}
}
prepareListOfFeatureInstance(featureInstanceList,featureList);
for (var key in featureAndFeatInstanceIdMap) {
loopWork(key);
}

Related

angular $q.all usage with multi layer of angular repeat

i have this piece of code
var final=[];
$http.get('/Scenarios/List/'+ id).success(function(resp){
angular.forEach(resp, function(value, key){
var scenario ={};
scenario.data=[];
angular.forEach(value.samples, function(b, i){
$http.get('/sample/One/'+ b.id).then(function(result){
var id= result.temp_id;
var temp ={};
$http.get('/a/list/'+ id).then(function(result){
temp.a=result;
});
$http.get('/b/list/'+ id).then(function(result){
temp.b-resutl;
});
scenario.data.push(temp);
})
});
final.push(scenario);
});
});
console.log(final); //no value
basically when i tried to get a "final" data for further parsing, i discovered it is empty. I think the problem could be due to the missing usage of $q.all, but i have checked many tutorials online, i cant figure out a proper usage of $q.all to solve my repeat usage of angular.forEach, in my case, there are two. Any thoughts?
I'd like to solve it like this, using $q.all is what you were missing as rightly mentioned in the question.
var final = [];
$http.get('/Scenarios/List/'+ id).success(function(resp){
processSamples(resp).then(function(final) {
console.log(final)
})
});
function processSamples(resp) {
var deferred = $q.defer();
angular.forEach(resp, function(value, key){
var scenario = {};
var promises = [];
scenario.data = [];
angular.forEach(value.samples, function(b, i){
promises.push($http.get('/sample/One/'+ b.id));
});
$q.all(promises).then(function(result) {
angular.forEach(result, function(res, index) {
var id= res.temp_id;
var temp = {};
var childpromises = [];
childpromises.push($http.get('/a/list/'+ id));
childpromises.push($http.get('/b/list/'+ id));
$q.all(childpromises).then(function(res) {
temp.a = res[0];
temp.b = res[1];
scenario.data.push(temp);
if(result.length === index + 1) {
final.push(scenario);
if(resp.length === key + 1) {
deferred.resolve(final);
}
}
})
})
})
});
return deferred.promise;
}
Notice how it resolves the returned promise once the uppermost loop is complete. Also, since there was no verifiable example provided, I might have left minor mistakes but it should probably give a good idea at least.

AngularJS How to execute function after some function finish?

I have some page which contain register with facebook button which I set hidden with ng-hide="fbLoggedIn" and form input which I set hidden with ng-show="fbLoggedIn"
My goal is register with facebook button will hide if fbLoggedIn set to true and form input will show if fbLoggedIn set to true.
register facebook button ng-click="registerFb()" execute this function
$scope.registerFB = function () {
authService.fbLogin();
$scope.fbLoggedIn = authService.fb_logged_in();
console.log($scope.fbLoggedIn); //this show false even `fb_access_token` not null
}
Here is my authService.fbLogin and authService.fb_logged_in function
authService.fbLogin = function () {
var FB = window.FB;
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
sessionService.set('fb_id', response.authResponse.userID);
sessionService.set('fb_access_token', response.authResponse.accessToken);
sessionService.set('fb_expiration_date', new Date(new Date().getTime() + response.authResponse.expiresIn * 1000).toISOString());
//console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Good to see you, ' + response.name + '.');
console.log(response);
});
} else {
console.log('User cancelled login or did not fully authorize.');
//console.log(response);
}
});
};
authService.fb_logged_in = function () {
if(sessionService.get('fb_access_token') != null){
return true;
}else {
return false;
}
};
In other function I try to check if fb_access_token is not null, just to make sure something wrong with my logic, and the result is true.
With above debuggin I can say that $scope.fbLoggedIn = authService.fb_logged_in(); execute before authService.fbLogin(); finish.
So how I can execute $scope.fbLoggedIn = authService.fb_logged_in(); after authService.fbLogin(); finish? maybe how to achieve my goal?
Alright. This can be achieved using promise. I don't know the parameters you have included in your autService service, so I will be making a factory of the same name with the new parameters that you might need to add.
Hence, according to me, this is how your factory should be.
angular.module('YourModuleName').factory('authService',['$http','$q',function($http,$q){
var obj = {};
obj.fbLogin = function () {
var defer = $q.defer();
var FB = window.FB;
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
sessionService.set('fb_id', response.authResponse.userID);
sessionService.set('fb_access_token', response.authResponse.accessToken);
sessionService.set('fb_expiration_date', new Date(new Date().getTime() + response.authResponse.expiresIn * 1000).toISOString());
FB.api('/me', function(response) {
console.log(response);
defer.resolve('Good to see you, ' + response.name + '.');
});
}
else {
defer.reject('User cancelled login or did not fully authorize.');
}
});
return defer.promise;
}
obj.fb_logged_in = function () {
if(sessionService.get('fb_access_token') != null){
return true;
}else {
return false;
}
};
return obj;
}])
And thus, the function call from the controller should be as follows.
$scope.registerFB = function () {
authService.fbLogin().then(function(response){
$scope.fbLoggedIn = authService.fb_logged_in();
console.log($scope.fbLoggedIn);
},function(error){
console.error("Error : ",error);
});
}
Note: CODE NOT TESTED.
Hence it would solve the problem with the best practices of angularJS
use the $rootscope to assign values they provide event emission/broadcast and subscription facility.

Controller behavior(different data from Factory) which depends on view

This is the Factory.
angular
.module('WordsFactory', [])
.factory('WordsFactory', WordsFactory);
function WordsFactory($http) {
exports = {
basics1Data: null
};
exports.getWordsBasics1 = function () {
return $http.get('data/basics1.json')
.success(function (data) {
exports.basics1Data = data;
})
.error(function (data) {
console.log('There was an error!', data);
});
};
exports.getWordsBasics2 = function () {
return $http.get('data/basics2.json')
.success(function (data) {
exports.basics1Data = data;
})
.error(function (data) {
console.log('There was an error!', data);
});
};
return exports;
}
Controller:
angular
.module('app.words', ['WordsFactory'])
.controller('WordsController', WordsController);
function WordsController(WordsFactory) {
var vm = this;
WordsFactory.getWordsBasics1()
.then(core);
function core(res){
//json data binds to variable
vm.basics1Data = res.data;
vm.points = 0;
getRandom();
//check if words are correct
vm.wordCheck = function () {
if(vm.wordInput === vm.test) {
alert('good');
getRandom();
vm.points++;
} else {
alert('bad');
getRandom();
}
};
function getRandom() {
var random = Math.floor(Math.random() * 6);
vm.test = vm.basics1Data[random];
}
}
}
In this project I will have multiple views. Every view will have the same structure and gonna do the same thing.
Views are something like categories.
-cat1
-cat2
-cat3
-and so on...
Only thing that would be different is json file.
for ex.
cat1 will have cat1.json
cat2 cat2.json etc.
Controller gonna do the same operations in every view, only on different data. How can I achieve that without copy and paste methods in controller f.ex.
WordsFactory.getWordsBasics1...
WordsFactory.getWordsBasics2...
The code that I have works, but I want to avoid copy-paste things.
Thank you in advance for any help.

how to handle multiple delete and displaying 1 message using delete service in angular JS

When i check all lists in table, and press delete button, A DELETE SERVICE will be called.(Using AngularJS)
Problem is, i am using a loop, and on successful delete and unsuccessful delete, i am getting alert multiple times.(No. of selection times)
And its not working properly, if place it out of loop because its Async Task.
Here is the code,
This is a controller which initiates a service.
$scope.confirmAction = function() {
var costsToDelete = [];
angular.forEach($scope.objects, function(cost) {
if (cost.selected == true) {
costsToDelete.push(cost);
}
});
$scope.deleted = true;
//need to put confirmation dialog here.
//URL: specific to timesheet deletion. it will be prefixed with constant url
var delRequestUrl = URLs.costsUrl + '/';
deleteService.deleteRecord($scope.objects, costsToDelete, delRequestUrl);
};
This is a service.
.service('deleteService', ['dataService', 'Constant.urls', 'Constants','$q','alerts',function(dataService, URLs, Constants, $q, alerts) {
var deleteService = {};
deleteService.deleteRecord = function(records, listOfRecordsToDelete, url) {
while (listOfRecordsToDelete.length > 0) {
var recordToBeDeleted = listOfRecordsToDelete.pop();
var index = listOfRecordsToDelete.indexOf(recordToBeDeleted);
var delRequestUrl = url + recordToBeDeleted.id;
var result = dataService.deleteObject(delRequestUrl);
result.success(function(data) {
Alert('success');
records.splice(index, 1);
});
result.error(function(data, status, headers, config) {
dataService.handleError(status,data);
Alert('error');
});
}
};
return deleteService; }])
I need a result like: Alert should display only once.
If all items are successfully deleted, then success or failure message.
Why dont you just create a boolean bit var status= false;//default value to true inside success callback handler and false inside error callback handler,
so once all calls are complete based on this bit you can alert success or failure
Angular JS Code:
.service('deleteService', ['dataService', 'Constant.urls', 'Constants','$q','alerts',function(dataService, URLs, Constants, $q, alerts) {
var statusBit = false; // status tracker
var deleteService = {};
deleteService.deleteRecord = function(records, listOfRecordsToDelete, url) {
while (listOfRecordsToDelete.length > 0) {
var recordToBeDeleted = listOfRecordsToDelete.pop();
var index = listOfRecordsToDelete.indexOf(recordToBeDeleted);
var delRequestUrl = url + recordToBeDeleted.id;
var result = dataService.deleteObject(delRequestUrl);
result.success(function(data) {
// Alert('success');
statusBit = true;
records.splice(index, 1);
});
result.error(function(data, status, headers, config) {
dataService.handleError(status,data);
//Alert('error');
statusBit = false;
});
if(statusBit){
Alert('success'); //console.log('successfully deleted');
}
else {
Alert('error'); // console.log('error while deleting');
}
};
return deleteService; }])
.service('deleteService', ['dataService', 'Constant.urls', 'Constants','$q','alerts',function(dataService, URLs, Constants, $q, alerts) {
var deleteService = {};
deleteService.deleteRecord = function(records, listOfRecordsToDelete, url) {
var overallResult = true;
while (listOfRecordsToDelete.length > 0) {
var recordToBeDeleted = listOfRecordsToDelete.pop();
var index = listOfRecordsToDelete.indexOf(recordToBeDeleted);
var delRequestUrl = url + recordToBeDeleted.id;
var result = dataService.deleteObject(delRequestUrl);
result.success(function(data) {
records.splice(index, 1);
});
result.error(function(data, status, headers, config) {
dataService.handleError(status,data);
overallResult = false ;
});
}
};
return deleteService; }])

Foreach loop in AngularJS and $http.get

I have problem with my AngularJS function. Data from first forEach is retrieved with $http.get and in second forEach, $scope.products isn't defined yet. I know that $http.get() is an asynchronous request and this is the point... But how to rewrite this function to work fine ?
$scope.getAll = function () {
var cookies = $cookies.getAll();
$scope.products = [];
var i = 0;
angular.forEach(cookies, function (v, k) {
console.log("important1:" + $scope.products);
console.log("key: " + k + ", value: " + v);
ProductsService.retrieve(k).then(function(response) {
$scope.products = $scope.products.concat(response.data);
$scope.products[i].quantity = v;
i++;
}, function (error) {
console.log('error');
});
});
console.log("important2:" + $scope.products);
angular.forEach($scope.products, function(value, key) {
$scope.total = value.quantity*value.price + $scope.total;
console.log("Quantiy: " + value.quantity);
console.log("Price: " + value.price);
});
console.log($scope.products);
console.log($scope.total);
};
I suggest that you use the $q.all().
More specifically, you would do:
$q.all([p1, p2, p3...]).then(function() {
// your code to be executed after all the promises have competed.
})
where p1, p2, ... are the promises corresponding to each of your ProductsService.retrieve(k).
Build up a list of service calls then call $q.all. Inside the then function you can put your second loop.
var listOfServiceCalls = [];
//Add to list of service calls
$q.all(listOfServiceCalls)
.then(function () {
//All calls completed THEN
angular.forEach($scope.products, function(value, key) {
$scope.total = value.quantity*value.price + $scope.total;
});
});

Resources