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

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.

Related

How to run the script after completion of foreach loop in angularjs

I want to run the script after the foreach loop complete its execution,
// Code goes here
var app=angular.module('test',[]);
app.controller('testCtrl',function($scope,$timeout,$q){
var values = {name: 'misko', gender: 'male'};
var log = [];
angular.forEach(values, function(value, key) {
$timeout(function () {
console.log('foreach running');
log.push(key + ': ' + value);
}, 50000);
});
$q.all(log).then(function(){
console.log(log);
})
})
In the above code the log array values are created in forEach loop I want to print the values in log array after the foreach execution completed, how should I do it?
Edit
when I used $timeout inside angulr.forEach the console.log fired before the completion of angular.forEach cycle, why so? where can I refer whether the call is synchronous or asynchronous?
var values = {name: 'misko', gender: 'male'};
var log = [];
var promises = [];
angular.forEach(values, function(value, key) {
var defer = $q.defer();
promises.push(defer.promise);
$timeout(function () {
console.log('foreach running');
log.push(key + ': ' + value);
defer.resolve();
}, 50000);
});
$q.all(promises).then(function(){
console.log(log);
})
since you have no asynchronized code running no need for $q so you can simply put console.log(log) after the loop, it will certainly be running after the loop is finished and the code would be like that
var app=angular.module('test',[]);
app.controller('testCtrl',function($scope,$timeout,$q){
var values = {name: 'misko', gender: 'male'};
var log = [];
for (var key in values) {
log.push({[key]:values[key]});
};
console.log(log);
});

Two $firebaseArrays on one page & one ctrl

I would like to use two different $firebaseArrays on one view with one controller. But only one of them works and the other only works if i put him in his own controller.
from my factory file:
.factory("AlphaFactory", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref('alpha/');
return $firebaseArray(ref);
}
])
.factory("BetaFactory", ["$firebaseArray",
function($firebaseArray) {
var ref = firebase.database().ref('beta/');
return $firebaseArray(ref);
}
])
and my controller:
.controller('DemoCtrl', function($scope, AlphaFactory, BetaFactory) {
$scope.alphaJobs = AlphaFactory;
$scope.addalphaJob = function() {
$scope.alphaJobs.$add({
Testentry: $scope.loremipsum,
timestamp: Date()
});
$scope.alphaJob = "";
};
$scope.betaJobs = BetaFactory;
$scope.addbetaJob = function() {
$scope.betaJobs.$add({
Testentry2: $scope.dolorest,
timestamp: Date()
});
$scope.betaJob = "";
};
)}
Are you sure it is not a simple matter of a promise has not finished?
var alphaJobs = AlphaFactory;
alphaJobs.$loaded().then(function() {
// Do something with data if needed
$scope.alphaJobs = alphaJobs;
});
var betaJobs = BetaFactory;
betaJobs.$loaded().then(function() {
// Do something with data if needed
$scope.betaJobs = betaJobs;
});

Protractor select values from drop down with promise

I'm running a protractor tests and filling in a form with dropdowns. How can I ensure that each dropdown has a promise that is resolved before moving onto the next one? Right now I'm using sleeps and know that can't be the right way.
Describe('Filling site utility form', function() {
beforeEach(function(){
var input_monthly = by.linkText('Input Monthly');
browser.wait(function() {
return browser.isElementPresent(input_monthly);
});
var field1 = element(by.id('field1'));
Test.dropdown(field1, 1);
var field2 = element(by.id('field2'));
Test.dropdown(field2, 1);
});
My dropdown function is:
dropdown = function(element, index, milliseconds) {
element.findElements(by.tagName('option'))
.then(function(options) {
options[index].click();
});
if (typeof milliseconds !== 'undefined') {
browser.sleep(milliseconds);
}
}
Not 100% sure what you mean but I suspect what you're interested in is this:
describe('Filling site utility form', function() {
beforeEach(function(){
var input_monthly = by.linkText('Input Monthly');
browser.wait(function() {
return browser.isElementPresent(input_monthly);
});
var promise1 = element(by.id('field1')).all(by.tagName('option')).get(1).click();
var promise2 = element(by.id('field2')).all(by.tagName('option')).get(1).click();
protractor.promise.all([promise1, promise2]).then(function() {
//what do you want to do next?
});
});
});

Angularjs how to stop infinity scroll when server has no more data to send

Based on THIS example which I have modified with an Ajax request to get the data Im struggling to find a way to stop it when the server has no more data to send.
I have tried to add a boolean variable in a service, and a $watch method in the directive but it is not working.
Is there a simple way to to achieve that ?
This is not my code but if there is no easy answer I can post my code with the changes I have done.
thanks for your help.
<div id="fixed" when-scrolled="loadMore()">
<ul>
<li ng-repeat="i in items">{{i.id}}</li>
</ul>
</div>
function Main($scope) {
$scope.data = { comments : [] }
$scope.loadMore = function(){
$http({
url: '/comment/next',
method: "POST"
})
.success(function(data){
for(var i=0; i<data.length; i++){
$scope.data.comments.push(data[i]);
}
});
}
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
I can only guess as to a solution without seeing more of your code (specifically what is returned by $http results), but I believe this sort of thing could work, depending on the structure of a comment object.
function Main($scope) {
$scope.data = { comments : [] }
$scope.scrollComplete = false;
$scope.loadMore = function(){
if($scope.scrollComplete || $scope.loading) { return; }
$scope.loading = true;
$http({
url: '/comment/next',
method: "POST"
})
.success(function(data){
for(var i=0; i<data.length; i++){
var totalComments = $scope.data.comments.length;
if($scope.data.comments[totalComments - 1].someID === data[i].someID){
$scope.scrollComplete = true;
}else{
$scope.data.comments.push(data[i]);
}
}
$scope.loading = false;
}).error(function(){ $scope.loading = false });
}
}
Just bear in mind that a solution like this isn't really elegant. What I like to do is allow an item ID to be passed to the API (i.e. your /comment/next), and treat that as the last grabbed item. So the API will only give me back everything after that. Using that method, you would simply have to pass the last comment ID to the API.

How do I do custom model data parsing in backbone.js?

While using backbone to hit an api, I've found that I need to only include some of the data in the response. The webserver is giving me back metadata in addition to data concerning my objects that I don't need.
The following solution works, but doesn't feel right. Is there a standard way of doing this?
var accountsCollection = new AccountsCollection();
accountsCollection.fetch({success : function(collection){
var results = new AccountsCollection();
collection.each(function(item){
results.add(new AccountModel({
id: item.toJSON().result[0].id,
messageText: item.toJSON().messageText,
address1: item.toJSON().result[0].address1,
address2: item.toJSON().result[0].address2
}));
});
onDataHandler(results);
}});
EDIT: This was my final solution based on the accepted answer:
parse: function(response) {
var accounts = [];
_.each(response['result'], function (account) {
accounts.push(account);
});
return accounts;
}
You could try overriding the Backbone.Collection.parse method and do some crazy underscore stuff. No idea if it fits your data..
var keysILike = ['foo', 'bar'];
AccountsCollection.extend({
parse: function(response) {
return _.compact(_.flatten(_.map(response, function (model) {
var tmp = {};
_.each(_.keys(model), function (key) {
if (_.contains(keysILike, key)) tmp[key] = model[key];
})
return tmp;
})));
}
});
With respect to #Sushanth's awesomeness you definitely want to use this solution:
var keysILike = ['foo', 'bar'];
AccountsCollection.extend({
parse: function(response) {
_.each(response, function (model) {
_.each(_.keys(model), function (key) {
if (!_.contains(keysILike, key)) delete model[key]
})
});
return response;
}
});

Resources