The value disappears in an array where elements are object - angularjs

Here's the code:
$scope.get_candidate();
console.log($scope.voteData);
$scope.get_candidate = function () {
var postData = {
department: $scope.voteData.department,
group: $scope.voteData.group
};
$http.post('/admin/r_candidate', postData)
.success(function (response) {
$scope.voteData.candidates = response.data.candidateInfos;
});
};
The console prints:
Object {candidates: Array[0], vote_begin: true, department: "机械与运载工程学部", vote_type: "预选", ballot_type: "记分"}
We can see candidates is an empty array and when I make a http post request with $scope.voteData, the candidates is empty. but when I see the detail:
Object
ballot_type:"记分"
candidates:Array[4]
department:"机械与运载工程学部"
vote_begin:true
vote_type:"预选"
__proto__:Object
We can see candidates has four elements which is what I expect because $http.post('/admin/r_candidate', postData) returns :
{status: 0, data: {candidateInfos: [,…], total: 4}}
data:{candidateInfos: [,…], total: 4}
I do not know why candidates becomes an empty array.

It's a recurring logic issue with async javascript.
When you run
$scope.get_candidate(); //async data inside
console.log($scope.voteData);
It will run the console.log() faster than you get result from the $http request
You can / should handle results inside the success function
$http.post('/admin/r_candidate', postData)
.success(function (response) {
console.log(response.data);
$scope.voteData.candidates = response.data.candidateInfos;
});
This said, depending on your angularjs version, you should use the .then syntax.

Related

store array into global variable angular

I am trying to save the value of parse array into global array.
but global array showing me undefined
dataUrl: string = "assets/data.csv";
private data:[];
dataInit(){
this.papa.parse(this.dataUrl, {
download: true,
complete: (result) => {
// result.data.push(this.data);
this.data = result.data
// console.log(result.data, "inside parser");
// console.log(this.data, "global array");
}
});
}
ngOnInit() {
this.dataInit();
console.log(this.data, "inside onInit");
}
Console
undefined "inside onInit"
There are two reasons for that -
You need to initilize the variable like this private data: Array<any>= [];
You are binding the value into asyn method and consoling the value in synchronous way.
The data will be available inside complete callback. So console.log(this.data) over there.
Reason: complete is a callback method which works asynchronously.
dataUrl: string = "assets/data.csv";
data = [];
dataInit(){
this.papa.parse(this.dataUrl, {
download: true,
complete: (result) => {
// result.data.push(this.data);
this.data = result.data
console.log(this.data);
}
});
}
ngOnInit() {
this.dataInit();
}
Change the initialization of the data property to something like
private data = [];
Or
private data: Array<T> = []
Instead of T type your array accordingly
Put the console log inside the complete function of the async code.
Because the papa.parse download code is asynchronous, the console log will show the initial value of data because the results are not ready yet.
Because this.papa.parse function is asynchronous, you can't get value of data variable right after calling dataInit... better to do inside complete callback
dataUrl: string = "assets/data.csv";
private data:[];
dataInit() {
this.papa.parse(this.dataUrl, {
download: true,
complete: (result) => {
this.data = result.data
this.toDo();
}
});
}
ngOnInit() {
this.dataInit();
}
toDo(){
console.log(this.data, "global array");
}

Construct array in nodejs through for loop

I am using sails (0.11.0) running on nodejs (6.9.1). I am trying to construct an array by filling it through for loop. I would send this completed array in response to the client. I have tried various methods as suggested by people here on Stack Overflow, for example
the discussion here suggested
for (var i = yearStart; i < yearEnd+1; i++) {
arr.push(i);
}
On this discussion, it is suggested to use:
var array = calendars.map(function(item) {
return item.id;
});
console.log(array);
Similarly I tried many methods but I am coming across the same issue that during the loop, the array gets filled but as soon as the loop is completed, the array gets empty because of asynchronous process and therefore I can not send the response. To tackle with this I tried checking the index inside the loop body and send response from inside the loop body itself through
var userArray = [];
_.each(users, function(user, index){
MySQLConnector.query('CALL user_image (?)', [user.id], function(err, userImage){
if(err){
return res.json({"status":"some_error"});
}else{
userID = user.id
userImageID = userImage[0][0].id;
var userInfo = {
userID: userID,
userImageID: userImageID
}
userArray.push(userInfo)
if(index == users.length - 1){
res.json({selectedUsers: userArray});
}
}
});
});
I am initiating an empty userArray and then iterate through users object where each element of the object is characterized by name user and an index. Through a MySQL query I am fetching the userImage object and in each iteration, I am creating an object called userInfo that consists of userID and userImageID. I am pushing this object into userArray. And after each iteratio of the for loop (_.each), I check if last index is reached. Once last index is reached, the final array is sent as response before loop body is complete.
Here too I have an issue that the array body is not always completely filled. The reason is due to asynchronous process, the index does not always follow the order 0,1,2,3,4,.... and it can start with any number and can jump to any index in the next iteration, for example the first index to start would be 4, the second would be 0, third would be 2 and so on. This sequence would be different for every time we run this for loop. For a user, it will appear to be a total random process. Therefore if users.length is 8, and current index is randomly 7 at third iteration, the condition index == users.length - 1 will be met and response will be sent just with an array consisting of 3 elements rather than 8.
Can someone suggest me a better and robust way to fill an array through the for loop in nodejs and send that array in response, so that all items are included in the array in their original order?
As you are using node js , it is better to use any promises library like bluebird or async to handle Async requests.
The reason your loop is not working as expected is because as you've pointed out, due to async requests taking time to resolve for which _.each loop is not waiting.
Using bluebird, it can be done with Promise.map method which works as explained below from the documentaion :
Given an Iterable(arrays are Iterable), or a promise of an Iterable,
which produces promises (or a mix of promises and values), iterate
over all the values in the Iterable into an array and map the array to
another using the given mapper function.
Promises returned by the mapper function are awaited for and the
returned promise doesn't fulfill until all mapped promises have
fulfilled as well. If any promise in the array is rejected, or any
promise returned by the mapper function is rejected, the returned
promise is rejected as well.
Hence, Using Promise.map your code can be updated like below :
var Promise = require("bluebird");
return Promise.map(users, function(user, index){
return MySQLConnector.query('CALL user_image (?)', [user.id], function(err, userImage){
if(err){
return Promise.reject({"status":"some_error"});
}else{
userID = user.id
userImageID = userImage[0][0].id;
var userInfo = {
userID: userID,
userImageID: userImageID
}
return userInfo;
}
});
})
.then(function (usersArray){
res.json({selectedUsers: usersArray});
})
.catch(function (err){
res.json(err);
});
You can execute loops with functions with callbacks synchronously using SynJS:
var SynJS = require('synjs');
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'tracker',
password : 'tracker123',
database : 'tracker'
});
function myFunction1(modules,connection,users) {
var ret=[];
for(var i=0; i<users.length; i++) {
connection.query("SELECT CONCAT('some image of user #',?) AS userImage", [users[i]], function(err, rows, fields) {
if (err) throw err;
ret.push({
id: users[i],
image: rows[0].userImage
});
modules.SynJS.resume(_synjsContext); // <-- indicate that callback is finished
});
SynJS.wait(); // <-- wait for callback to finish
}
return ret;
};
var modules = {
SynJS: SynJS,
mysql: mysql,
};
var users = [1,5,7,9,20,21];
SynJS.run(myFunction1,null,modules,connection,users,function (ret) {
console.log('done. result is:');
console.log(ret);
});
Result would be following:
done. result is:
[ { id: 1, image: 'some image of user #1' },
{ id: 5, image: 'some image of user #5' },
{ id: 7, image: 'some image of user #7' },
{ id: 9, image: 'some image of user #9' },
{ id: 20, image: 'some image of user #20' },
{ id: 21, image: 'some image of user #21' } ]

chaining http post and q service in angular in serial fashion

I have this code in angular,
$http({
method:'POST',
url :'someURL', //returns an array of urls [url1, url2, url3..]
data : dataObj
})
.then(function(response) {
var items = response.data;
var promises = [];
$scope.output =[];
items.forEach(function(el){
return promises.push($http.get(el)); //fills the promise[] array
});
var ignore = function(x) { return x.catch(function(){}); } // To ignore if promise does not get resolved (only accept responses with status 200)
var all = $q.all( promises.map(ignore) ); //chaining promises array
all.then(function success(d){
console.log($scope.output); //want the output to be ["text1", "text2", "text3"...]
});
for (var i=0; i < promises.length ; i++){
promises[i].then(success).catch(function (){
});
function success(r){
$scope.output.push(r.data.text); //{text: "text1"}
}
}
});
The result of this operation is stored in $scope.output. On executing I'm getting output as ["text2", "text3", "text1" ...] which is not in a serial fashion. My question is how I can make this execute in a serial fashion so that the output would be ["text1", "text2", "text3" ...]
Replace your last for loop with the following:
angular.forEach(promises, function(promise, index){
promise.then(success).catch(function (){});
function success(r){
$scope.output[index] = r.data.text;
}
});
Due to closure paradigm the index variable will be available in the success handler upon promise resolution no matter in which order the promises get resolved and the results will be placed to the output array in the order of the original promises.
Didn't test it, but from first view I'd say you need to put the for() loop inside the all.then().
all.then(function success(d){
console.log($scope.output);
for (var i=0; i < promises.length ; i++) {
promises[i].then(success).catch(function () { });
function success (r) {
$scope.output.push(r.data.text);
}
}
});
Because otherwise you loop through partially unresolved promises. Those that resolve earlier will skip ahead in the queue.
Having the for() loop inside the all.then() you make sure that all promises have resolved already and will add themselves to the output list when they are called with promises[i].then(success).
IMO,you should not use callbacks inside for loop. I think, it is causing this behavior. I hope it will work. No need to add last for loop.
var all = $q.all( promises.map(ignore) );
all.then(function success(d){
d.forEach(function(res){
$scope.output.push(r.data.text);
});
console.log($scope.output);
});

How can I wait for $http response before continuing with angular.forEach loop

I'm making an AJAX request for each item in a loop, the end REST service can only perform one request at a time so I need the loop to wait for each request to complete before continuing with the next. How do I do this?
For reference, the end service is performing update tasks on DynamoDB tables - only one table can be modified at once hence my requirement to wait until I get a response before continuing. I could send them all to the server in one hit and handle there, although that makes it hard to receive feedback when each update is completed.
angular.forEach($scope.someArray,
function (value) {
var postdata = {
bla: value
};
$http.post('/some_url', postdata)
.then(
function(result) {
console.log("Success.");
},
function(data) {
console.log("Failure.");
}
);
}
);
Do you really need the forEach? I would not use it and go for something like that:
function req(arr) {
if (angular.isArray(arr) && arr.length > 0) {
var postdata = {
bla: arr[0]
};
$http.post('/some_url', postdata)
.then(
function(result) {
console.log("Success.");
arr.shift();
req(arr);
},
function(data) {
console.log("Failure.");
// if you want to continue even if it fails:
//arr.shift();
//req(arr);
}
);
}
}
req($scope.someArray);
If you really must make one request at a time (are you sure there isn't a more efficient alternative?), then you'll have to use something other than Angular.forEach.
What about something like this (you will need to inject $q):
function doWhateverWithAll(someArray) {
// Mark which request we're currently doing
var currentRequest = 0;
// Make this promise based.
var deferred = $q.deferred();
// Set up a result array
var results = []
function makeNextRequest() {
// Do whatever you need with the array item.
var postData = someArray[currentRequest].blah;
$http.post('some/url', postData)
.then( function (data){
// Save the result.
results.push(data);
// Increment progress.
currentRequest++;
// Continue if there are more items.
if (currentRequest < someArray.length){
makeNextRequest();
}
// Resolve the promise otherwise.
else {
deferred.resolve(results);
}
});
// TODO handle errors appropriately.
}
// return a promise for the completed requests
return deferred.promise;
}
Then, in your controller/service, you can do the following:
doWhateverWithAll($scope.someArray)
.then(function(results){
// deal with results.
});

pushing ajax json responses in array

I try to iterate throug an array of ids and make a ajax request for each id. subsequentely each response object is pushed in an array/ so far no problem, however, the problem starts when i try to access the responses in the array. the strange thing is that in the console log the responses are shown (ouside the array though, see below) but the properties of the array objects are empty/ it seems i generated an empty object with some data attached to it/ my question is how can i access the objects that are in (or not in?) the array
var getAssoc = {
returnProds: function (idCache) {
var id = idCache;
var prodData = [];
var counter = id.length;
$.each(id, function (i) {
$.ajax({
url: "myurl.php?",
data: {
'id': id[i]
},
success: function (data) {
prodData[i] = data;
counter--;
if (counter === 0) console.log(prodData);
},
})
});
}
};
console log looks like this. testing for number of properties returns 0
[]
0 Object { array={...}}
1 Object { array={...}}
2 Object { array={...}}
3 Object { array={...}}
In you success callback Try parsing the response coming from your server:
jQuery.parseJSON(data);

Resources