AngularJS promise after foreach - javascript

I have a problem with my promise after a foreach,
I would like to remove the object which has no parent from my database.
I read this post and I tried many things, for example this:
controller
project.getAll().then(function(results) {
return $scope.array_projects = results;
}).then(function(array_projects) {
$scope.results = [];
console.log(array_projects);
angular.forEach(array_projects, function(result) {
console.log(result);
var project = new Project(result);
var promise = project.getOne(result.ParentId);
$scope.result.push(promise);
promise.then(function(results) {
return results.length ? project:null;
});
// if(result.ParentId !== null) {
// console.log('ParentId');
// var promise = project.getOne(result.ParentId).then(function(results) {
// //console.log(results);
//
// if (results.length === 0){
// console.log('remove children');
// } else {
// $scope.results.push(project);
// console.log('parent exist, children added!');
//
// }
//
// });
//
// }
});
// after forEach
$q.all($scope.results).then(function(results){
console.log($scope.results);
// result is an array of mixed Project object or null value, just remove the null value and you're fine :)
var trueResults = [];
for(var i = 0; i < results.length; i++){
if(results[i]){
trueResults.push(results[i]);
}
}
// now use trueResults
console.log(trueResults);
})
});
model
var Project = function (properties) {
// Model
this.description = null;
this.file = null;
this.name = null;
this.ParentId = null;
this.path = null;
angular.extend(this, properties);
};
// With service
// ------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
Project.prototype.getAll = function () {
return ProjectService.getAll();
};
Project.prototype.getOne = function (id) {
return ProjectService.getOne(id);
};
project.service.js
this.getAll = function (params) {
var projects = [];
return db.selectAll('projects').then(function(results) {
for(var i=0; i < results.rows.length; i++){
projects.push(results.rows.item(i));
}
return projects;
});
};
this.getOne = function (id) {
var project = [];
console.log(id);
return db.select('projects', {
"id": id
}).then(function(results) {
for(i=0; i < results.rows.length; i++){
project.push(results.rows.item(i));
}
return project;
})
};
But my console.log($scope.results); show '[]'
In my BDD I have :
id/id/ParentId/name/file/date
5 5 test crm-site.net /release.txt 2016-04-26 08:43:17
6 6 5 test2 crm-site.net /release.txt 2016-04-26 08:43:19
7 7 test3 crm-site.net /release.txt 2016-04-26 08:43:20
8 8 7 test4 crm-site.net /release.txt 2016-04-26 08:43:21

It seems like you are misunderstanding how to use $q.all.
Each time you call a project.getOne, you don't know when the result will come as it is asynchonious.
If you want to use the $q.all, you should push all the project.getOne in an array and $q.all(myArray). It is normal that your code only show [] because the .then aren't called yet and the push method haven't been called yet when you go through the console.

You wasn't so far of what you were trying just update your code with :
// in the foreach
if(result.ParentId){
var promise = project.getOne(result.ParentId);
$scope.result.push(promise);
promise.then(function(results) {
return results.length ? project:null;
}
}
// after forEach
$q.all($scope.results).then(function(results){
// result is an array of mixed Project object or null value, just remove the null value and you're fine :)
var trueResults = [];
for(var i = 0; i < results.length; i++){
if(results[i]){
trueResults.push(results[i]);
}
}
// now use trueResults
})
The explanation is that $q.all take an array of promise not objects, he can't wait that you fill an array, because he can't know what criteria makes you're array "ready" unlike a promise where the resolve will be called.

Related

how to return a Promise.all().then to a function

I am trying to return a Promise.all() to a function. I tried different ways but it is showing errors
here is my code
// All this iam doing in my server side controller
Promise = require('bluebird');
function countByTitle(accrdnArray) {
console.log(accrdnArray) // array of objects is printing here
var arrayCount = countfunc(accrdnArray);
console.log(JSON.stringify(arrayCount)) // here it is not printing showing error
}
function countfunc(accrdnArray) {
var array = [];
for (var i = 0; i < accrdnArray.lenght; i++) {
var heading = accrdnArray[i].name;
array.push(mongoCount(heading));
}
return Promise.all(array).then(resultantCount => {
console.log(JSON.stringify(resultantCount)); // resultanCout printing here
return resultantCount
})
// Here i want to return the resultantCount to above function.
}
function mongoCount(heading) {
var mongoQuery = {
"ProCategory.title": heading
}
return Collection.count(mongoQuery).then(function(count) {
return {
name: categoryTitle,
count: count
};
});
}
return Promise.all(array).then(resultantCount =>{
console.log(JSON.stringify(resultantCount )); // resultanCout printing here
return resultantCount;
});
This part of the code returns a Promise object. So you have to use .then() again in your countByTitle function.
function countByTitle(accrdnArray) {
console.log(accrdnArray) // array of objects is printing here
var arrayCount = countfunc(accrdnArray);
arrayCount.then(function(count) {
console.log(JSON.stringify(count));
});
}

How to fix my promise return here for this function?

I have an array of tags which may contain up to 3 items.
Next I pass tags into my getTagQuotes function and am trying to set a promise variable to it. Right now I'm getting promise is undefined.
// If there are tags, the wait for promise here:
if (tags.length > 0) {
var promise = getTagQuotes(tags).then(function() {
console.log('promise =',promise);
for (var i=0; i<tweetArrayObjsContainer.length; i++) {
chartObj.chartData.push(tweetArrayObjsContainer[i]);
}
chartDirective = ScopeFactory.getScope('chart');
chartDirective.nvd3.drawChart(chartObj.chartData);
});
}
else {
chartDirective = ScopeFactory.getScope('chart');
chartDirective.nvd3.drawChart(chartObj.chartData);
}
^ The goal above is to make sure that arrays in tweetArrayObjsContainer have been filled and pushed into chartObj.chartData before I draw my nvd3 chart.
Below is my getTagQuotes function which does another loop through the tags (up to 3) and calls another service GetTweetVolFactory.returnTweetVol which makes the actual API call to retrieve data.
I have code which checks if the we're on the final loop step, then calls the formatTagData function.
Inside formatTagData is where I fill the tweetArrayObjsContainer.
// Check for and GET tag data:
////////////////////////////////////////////////////////////////////
function getTagQuotes(tags) {
console.log('getTagQuotes called with',tags);
var deferred = $q.defer(); // <- setting $q.defer here
var url = 'app/api/social/twitter/volume/';
for (var i=0; i<tags.length; i++) {
var loopStep = i;
rawTagData = [];
GetTweetVolFactory.returnTweetVol(url+tags[i].term_id)
.success(function(data, status, headers, config) {
rawTagData.push(data);
if (loopStep === (rawTagData.length - 1)) {
// formatTagData fills the tweetArrayObjsContainer with data:
formatTagData(rawTagData);
deferred.resolve(); // <-- last step, so resolve
return deferred.promise;
}
})
.error(function(data, status) {
return 'error in returning tweet data';
});
}
function formatTagData(rawData) {
tweetArrayObjsContainer = [];
for (var i=0; i<rawData.length; i++) {
var data_array = [];
var loopNum = i;
_(rawData[i].frequency_counts).reverse().value();
for (var j=0; j<rawData[loopNum].frequency_counts.length; j++) {
var data_obj = {};
rawData[loopNum].frequency_counts[j].start_epoch = addZeroes(rawData[loopNum].frequency_counts[j].start_epoch);
data_obj.x = rawData[loopNum].frequency_counts[j].start_epoch;
data_obj.y = rawData[loopNum].frequency_counts[j].tweets;
data_array.push(data_obj);
}
var tweetArrayObj = {
"key" : "Quantity"+(i+1),
"type" : "area",
"yAxis" : 1,
"color" : tagColorArray[i],
"values" : data_array
};
tweetArrayObjsContainer.push(tweetArrayObj);
}
}
}
Your getTagQuotes function should return a promise with the $q service, check it out.
It should look like this:
function getTagQuotes(tags) {
var deferred = $q.defer();
(...)
// When the action is finished here, call resolve:
deferred.resolve();
(...)
// And in the end, return the promise
return deferred.promise;
}
And finally, in your main code, you'll do:
var promise = getTagQuotes(tags).then(function(){
console.log('promise =',promise);
// Fill chartObj with tweet data arrays
(...)
});
This function has no return statement.
function getTagQuotes(tags)

How to pass a promise to a controller in AngularJS

I'm having trouble understanding promises. I have a function $scope.startJob that when called, it calls areThereAvailableSegments in the SegmentsService. The idea is that areThereAvailableSegments should return true or false, so that I can handle this behavior in the controller, but not sure how to do this.
areThereAvailableSegments calls getSegments(), which requests text segments from the database. getSegments returns a promise, and I'm trying to handle this promise with .then in areThereAvailableSegments, but not sure how to pass to the controller.
Here's my Segments factory, that returns the $resource object, segmentsfactory.js:
angular.module('appApp')
.factory('SegmentsFactory', function ($resource) {
return $resource('http://localhost:3000/api/v1/segments/:id');
});
Here's my Segments service, segmentsservice.js:
angular.module('appApp')
.service('SegmentsService', function (SegmentsFactory) {
// gets segments from mongo
this.getSegments = function(jobId) {
return SegmentsFactory.query({ job_id: jobId }).$promise;
};
// should return true/false if there are available segments
this.areThereAvailableSegments = function(jobId) {
var dump = [];
// queries for all segments
this.getSegments(jobId)
.then(function(segments) {
// dumps segments status in a temp array
for (var i = 0; i < segments.length; i++) {
dump.push(segments[i].status);
}
// returns true if there is an 'available' in the temp array
if (dump.indexOf('available') >= 0) {
return true;
} else {
return false;
}
});
};
});
And here's my controller, main.js:
$scope.startJob = function() {
if (SegmentsService.areThereAvailableSegments(jobId)) {
console.log('there are available segments, then trigger texteditor');
} else {
console.log('no segments to translate');
}
};
When executing, console shows "no segments to translate", because SegmentsService is not returning true or false.
How to solve this?
the function areThereAvailibleSegments should return a promise as well.
And then in your controller you have a then case and have your if-else there.
this.areThereAvailableSegments = function(jobId) {
var dump = [];
// queries for all segments
return this.getSegments(jobId) //Return here
.then(function(segments) {
// dumps segments status in a temp array
for (var i = 0; i < segments.length; i++) {
dump.push(segments[i].status);
}
// returns true if there is an 'available' in the temp array
if (dump.indexOf('available') >= 0) {
return true;
} else {
return false;
}
});
};
since your success function returns true or false the promise from areThereAvailibleSegments-function will have that boolean.
Then in you controller:
SegmentsService.areThereAvailableSegments(jobId).then(function(available){
if (available) {
console.log('there are available segments, then trigger texteditor');
} else {
console.log('no segments to translate');
}
});
Theres no reason to have both the service and factory. Remove the factory and move that code into the service. As for your problem, you will have to use the $q module to return a promise. https://docs.angularjs.org/api/ng/service/$q
this.areThereAvailableSegments = function(jobId) {
var dump = [];
var deferred = $q.deferred;
// queries for all segments
this.getSegments(jobId)
.then(function(segments) {
// dumps segments status in a temp array
for (var i = 0; i < segments.length; i++) {
dump.push(segments[i].status);
}
// returns true if there is an 'available' in the temp array
if (dump.indexOf('available') >= 0) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
});
return deferred.promise;
};
You will then have to deal with the promise in the controller.
I would set it up like this...complete the promise in your factory and then call the data in the controller.
In your services js:
.factory('SegmentsFactory', function ($resource) {
var APIRequest = {};
APIRequest.getSegments = function(id){
segmentsAPI = $resource('http://localhost:3000/api/v1/segments/'+id'', ;
return segmentAPI.get().$promise.then(function(segments){
console.log(segments);
return segments;
});
};
return APIRequest;
}]);
And then in your controller:
var dump = [];
$scope.startJob = function() {
if (SegmentsService.areThereAvailableSegments) {
APIRequest.getSegments($scope.id).then(function(data){
if (data.error){
//error stuff here
} else {
if (data.segments.length > 0){
for(var i=0; i < segments.length; i++){
console.log(segments);
dump.push(segments[i].status);
}
};

Search in array of objects with object javascript

I have an array like this
var userdata = [
{"id":1,"gender":"M","first":"John","last":"Smith","city":"Seattle, WA","status":"Active"},
{"id":2,"gender":"F","first":"Kelly","last":"Ruth","city":"Dallas, TX","status":"Active"},
{"id":3,"gender":"M","first":"Jeff","last":"Stevenson","city":"Washington, D.C.","status":"Active"},
{"id":4,"gender":"F","first":"Jennifer","last":"Gill","city":"Seattle, WA","status":"Inactive"}
]
I need to filter this array on some conditions. The form of these conditions are like this.
var search_object = {gender:"M",city:"Seattle, WA"}
// Gender = M and city = 'Seattle, WA'
var search_object1 = {gender:"M"}
var search_object2 = {city:"Seattle, WA"}
// This is same as above
var search_array = {status:["Active","Inactive"]}
// Status Active or Inactive
var search_array = [{status:"Active"},{status:"Inactive"}]
// Same as above
var search_object1 = {gender:"F"}
var search_array = [{status:"Active"},{status:"Inactive"}]
//Gender = F and status = Active or Inactive
var search_object = {gender:"F"}
var search_array = [{status:["Active","Inactive"]}]
// same as above
I have tried looping but failed. Please help or suggest or provide some proper links to get help.
The following code covers all the cases you mentioned.
function search(searchObj, data) {
if(searchObj instanceof Array) {
return data.reduce(function(prev, current, index, array) {
return prev.concat(search(current, data));
}, []);
} else {
var results = data.filter(function(el) {
for(var prop in searchObj) {
if(searchObj[prop] instanceof Array) {
if(searchObj[prop].indexOf(el[prop]) == -1) {
return false;
}
} else
if(el[prop] !== searchObj[prop]) {
return false;
}
}
return true;
});
return results;
}
};
search(search_object, userdata);
Here is the working example in JSFiddle.
And here are some links to the functions I've used above:
Array.prototype.reduce()
Array.prototype.concat()
Array.prototype.filter()
Array.prototype.indexOf()
Just what RGraham said in the comments, you can use the filter function on arrays.
var search_object = {gender:"M",city:"Seattle, WA"};
var filtered = userdata.filter(function(obj){
return (obj.gender === search_object && obj.city === search_object.city)
});
filtered[0];//Array with objects that return true;

ExpressJS require Javascript prototyped object in another file

I have a file that looks like this:
function UserController(){};
UserController.prototype = {
theData: [],
findAll: function(callback) {
callback( null, this.theData );
},
findByID: function(id, callback) {
var result = null;
var count = theData.length;
for (var i = 0; i < count; i++) {
if (this.theData[i]._id == id) {
result = this.theData[i];
break;
}
}
callback(null, result);
},
findByName: function(name, callback) {
var result = null;
var count = theData.length;
for (var i = 0; i < count; i++) {
if (this.theData[i].name == name) {
result = this.theData[i];
break;
}
};
callback(null, result);
},
save: function(users, callback) {
var user = null;
if(typeof(users.length) === 'undefined') {
users = [users];
}
var count = users.length;
for (var i = 0; i < count; i++) {
user = users[i];
user._id = userCounter++;
user.created_at = new Date();
};
callback(null, users);
}
};
When I inspect the object (based off this), it tells me that it is function UserController(){} and has no methods or properties.
I've never used prototyping in Javascript and I can't see a thing wrong with what I've done here. It should be noted that I'm using this with Node.js and Expressjs but I don't believe that would be causing this problem.
EDIT
For brevity's sake I only posted what I was currently using, but I have also had the exact same problem using the file like this:
var userCounter = 1;
function UserController(){};
UserController.prototype.theData = [];
UserController.prototype.findAll = function(callback) {
callback( null, this.theData );
};
UserController.prototype.findByID = function(id, callback) {
var result = null;
var count = theData.length;
for (var i = 0; i < count; i++) {
if (this.theData[i]._id == id)
{
result = this.theData[i];
break;
}
};
callback(null, result);
};
UserController.prototype.findByName = function(name, callback) {
var result = null;
var count = theData.length;
for (var i = 0; i < count; i++) {
if (this.theData[i].name == name) {
result = this.theData[i];
break;
}
};
callback(null, result);
};
UserController.prototype.save = function(users, callback) {
var user = null;
if(typeof(users.length) === 'undefined') {
users = [users];
}
var count = users.length;
for (var i = 0; i < count; i++) {
user = users[i];
user._id = userCounter++;
user.created_at = new Date();
};
callback(null, users);
};
One more edit
If I put this in the same file and Inspect it I get the results I would expect:
function Test(){};
Test.prototype = {
theData: [],
hello: function(){
return "Hello World!";
}
};
I don't see the difference and I don't see an error in my UserController code.
This link helped me figure it out.
Turns out it WAS a problem with Expressjs. When using another file, you have to specifically call export on methods and objects you want to use with require. Or alternately, you can just call this.blahblah = function(){}; instead of export. Then when you require a file, it automatically turns it into an object.
EDIT
I found a different solution. It was as simple as changing my require call to look like this: var userController = new (require('./controllers/user').UserController);
After that, I can use Prototyped functions and objects just fine which makes the file much more readable and easier to understand.
Yes, UserController is a function. Does it have properties? Yes, it has two properties, namely length and prototype. All functions in JavaScript have these two properties. Why don't you see these properties? Because they are not enumerable properties. Properties in JavaScript can have one or more attributes. In ES3 they are ReadOnly, DontEnum, and DontDelete. In ES5 they are configurable, writable, enumerable, get, set, and value.
In either version, length and prototype are not enumerable, so your inspector will not show them to you.
Incidentally all the "methods" you wrote are properties of the object UserController.prototype, not UserController.

Categories