2

in my controller i have a function

$scope.orderHistory = {};
$scope.results = {};

var getHistory = function (max,offset)
 {
    //do some stuff
    $scope.orderHistory = data.entities;

    angular.forEach($scope.orderHistory,function(value)
    {
        $scope.results = getResults(value.id);
        console.log($scope.results);

    });

 }


 var getResults = function(id)
 {

    api.getHistoryData(id)
        .then ( function (results)
        {
            return results.data;

        });


 };

the problem i run into is the getResults function does not seem to actually return the data and have it set to $scope.results inside the getHistory function.

When i do a console.log in (results.data) inside the .then part of getResults function i see the results. Can anyone help me understand why it is not returning the results even though it exists and what i can do to fix it

6
  • You are expecting $scope.resulsts the array of results? Commented Aug 14, 2014 at 17:57
  • yes $scope.results should be the results and when i do a console.log($scope.results) i should see the results returned from getResults() Commented Aug 14, 2014 at 17:58
  • is orderHistory array or is it an dictionary? Commented Aug 14, 2014 at 18:04
  • orderHistory is an array Commented Aug 14, 2014 at 18:09
  • do you see an empty object in your view? if you console log the results in means you have it and everything works in the controller. Commented Aug 18, 2014 at 21:54

6 Answers 6

10
+25

The function in then(function(){...}) is executed asynchronously, and the containing getResults function won't wait for it to return. More specifically, api.getHistoryData seems to return a promise.

You can use Angular's data binding to work around this:

var getHistory = function (max,offset)
 {
    //do some stuff
    $scope.orderHistory = data.entities;

    angular.forEach($scope.orderHistory,function(value)
    {
        getResults(value.id);    
    });

 }

var getResults = function(id)
 {   
    api.getHistoryData(id)
        .then ( function (results) {
            $scope.results = results.data;
        });
 };

And then in your template use something like:

<button ng-click="getHistory(0, 0)"></button>
{{results}}
Sign up to request clarification or add additional context in comments.

5 Comments

This isnt anything to do with the view though. Im trying to get the results from getResults function and use it as part of my getHistory function
@Yeak I updated my answer, you'll need to understand asynchronous javascript (see first link in my answer) to understand why your approach didn't work.
I understand asynchronous vs synchronous. I am new to angular though and your answer doesnt really tell me how to solve the issue.
Well, I mean you also need to know what a callback function and a promise is. Also, as long as you don't tell me why my answer isn't working for you there is little I can do...
this answer is correct...you can't return a value from within a callback in the then for api.getHistoryData and expect getResults to return it to getHistory...
4

Lets see if I can clarify how promises work, and what might be throwing you off here:

When you say:

$scope.results = getResults(value.id);

That means, assign the return value of getResults to the $scope.results variable. Now let us take a look at your getResults function.

 var getResults = function(id) {
    api.getHistoryData(id)
        .then(function (results) {
            return results.data;
    });
 };

Now notice a few things:

  1. The getResults function does not have a return statement. That means there is nothing returned from getResults to be assigned to $scope.results.
  2. The getResults function itself makes an asynchronous request. That means, if when the function executes, and when the results come back are two different points in time.
  3. Finally, your return statement is not inside the getResults function (technically it is), but is in fact inside a function inside the getResults function. Thus, the return results.data is for the function(results) {} and not for getResults
  4. Also, notice if it had worked as you would have expected it to, you would keep overriding the value of $scope.results each time the server responded. That's probably not what you want

Let us modify the getResults function to clarify this:

angular.forEach($scope.orderHistory, function(value) {
    console.log('1 - Get results for ', value.id);
    getResults(value.id);    
});
 var getResults = function(id) {
    console.log('2 - GetResults called');
    api.getHistoryData(id)
        .then(function (results) {
            console.log('3 - Get results has data');
            return results.data;
    });
 };

If we had code like the above, then the console.logs for 1 and 2 would get printed first, all together, and then at some later point, all the 3's would get printed together. This is because JavaScript is asynchronous and non-blocking, which means it will continue executing without waiting for the responses to come back.

So instead, to get this to work like you expect it to, you need to leverage Promises in AngularJS, which allow you a hook to know when the server response has come back. It offers us a way of working with asynchronous events across functions. So if you modify your code as follows:

angular.forEach($scope.orderHistory, function(value) {
    getResults(value.id).then(function(results) {
        $scope.results[value.id] = results.data;
    });    
});
 var getResults = function(id) {
    return api.getHistoryData(id);
 };

So we return a promise from getResults, and then when the promise is completed, we use the value and set it on $scope.results in the main function.

Hope this makes things clearer.

Comments

1

I think you aren't using the promise correctly:

$scope.orderHistory = {};
$scope.results = {};

var getHistory = function (max,offset)
 {
    //do some stuff
    $scope.orderHistory = data.entities;

    angular.forEach($scope.orderHistory,function(value)
    {
        // get results returns a promise
        // we use the promise to set $scope.results
        getResults(value.id).then(function(results){
            $scope.results = results.data;
        });      
    });

 }

 // returns a promise
 var getResults = function(id)
 {
    return api.getHistoryData(id);
 };

$scope.results will be changed only after the promise is resolved.

Also, the way it is currently written, $scope.results will be overwritten for each value.id. If that is not your intention I'd turn it into an array and push each new value into it.

Comments

1

Lets be clear, your orderHistory and results variables are objects, not arrays. The angular.forEach function allows you to iterate over both arrays and object properties; it does not mean the object being iterated is an array. Elad is correct in stating that your results variable will be overwritten for each iteration.

If you do want to keep all the results from your getResults function, you need to instantiate results as an array; and to have your view reflect the results, push them into your scope variable directly from the async result.

$scope.orderHistory = {};
$scope.results = [];

var getHistory = function (max,offset)
 {
    //do some stuff
    $scope.orderHistory = data.entities;

    angular.forEach($scope.orderHistory,function(value)
    {
        getResults(value.id);
    });

 }


 var getResults = function(id)
 {
    api.getHistoryData(id)
        .then ( function (results)
        {
            $scope.results.push(results);
        });
 };

Comments

0

just fix getResults, add return to it

var getResults = function(id)
 {
    return api.getHistoryData(id)
        .then ( function (results)
        {
            return results.data;

        });
 };

Comments

0

Here are 2 points. First, getResults returns nothing, it just process chain of promisses. We can change getResults to return promiss which will return result:

var getResults = function(id)
 {
    return api.getHistoryData(id)
        .then ( function (results)
        {
            return results.data;

        });
 };

Second, from angular version 1.2 (not exactly sure if from this version) angular do not resolve promisses automaticaly so you must to do it yourself:

angular.forEach($scope.orderHistory,function(value)
{
    getResults(value.id).then(function(results){
       $scope.results = results;
       console.log($scope.results);
   })
});

The whole code:

$scope.orderHistory = {};
$scope.results = {};
var getHistory = function (max,offset)
 {
    //do some stuff
    $scope.orderHistory = data.entities;
    angular.forEach($scope.orderHistory,function(value)
    {
        getResults(value.id).then(function(results){
           $scope.results = results;
           console.log($scope.results);
       })     
    });
 }
 // returns a promise
 var getResults = function(id)
 {
   return api.getHistoryData(id)
        .then ( function (results)
        {
            return results.data;
        });
 };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.