10

I am trying to return an object from a function to ng-repeat:

<ul ng-controller="Users" ng-repeat="user in users">
    <li ng-controller="Albums" ng-repeat="album in getAlbumList(user.name)">
        {{album.title}}
    </li>
</ul>

.controller('Albums', ['$scope','Imgur', function($scope, Imgur) {
    $scope.getAlbumList = function(user) {
        Imgur.albumList.get({user:user},    
            function(value) {
                return value.data;
                console.log('success');
            },
            function(error) {
                console.log('something went wrong');
            }
        );
    }
}]);

.factory('Imgur', function($resource, $http) {
    return {
        albumList : $resource('https://api.imgur.com/3/account/:user/albums'),
        album : $resource('https://api.imgur.com/3/account/:user/album/:id')
    }
});

This approach however crashes my browser on every page load.

What is the right way to do this?

2 Answers 2

12

So, you have ng-repeat="album in getAlbumList(user.name)", which is an Angular expression. What ng-repeat does is evaluates getAlbumList(user.name) on every digest. This is just like any other expression in a view, such as ng-show or whatever, and those expressions get evaluated all the time. If your expression is slow to evaluate hundreds of times, you shouldn't have it in an expression like this.

What you have here is an Ajax call in that function. Even if you somehow had a blocking synchronous Ajax call, it would be orders of magnitude too slow. Expressions should just access data already at hand, or maybe run simple code to sort and filter. They should never try to make Ajax calls.

But that's assuming it's synchronous Ajax, which this isn't. The expression simply calls getAlbumList and assumes the result is an array and uses it for ng-repeat. That function returns nothing, so it returns undefined, and you get nothing.

So to answer your question, what you should do is, put an empty array on scope, use that from the ng-repeat, and fill it in via Ajax. The beauty of Angular means the data will just "pop in" in the view once it's there. Here's some view code:

<ul>
  <li ng-repeat="album in albums">{{album.title}}</li>
</ul>

And some controller code:

$scope.albums = [];
Imgur.albumList.get({user:user},
    function(value) {
        $scope.albums = value.data;
        console.log('success');
    },
    function(error) {
        console.log('something went wrong');
    }
);

Which is basically the code you already had, just not run from a function, but run right in the constructor of the controller.

Edit

I think your fundamental misunderstanding is thinking that returning something from an ngResource callback will in turn return it from the containing function (getAlbumList). That's not the case. The containing function runs, returns nothing, then later on your callback runs, returns something, but no one is using that return value.

Sign up to request clarification or add additional context in comments.

1 Comment

+1 That was a really helpful explanation why that is the wrong way to do this. Thankyou!
6
<ul ng-controller="Users">
     <li ng-repeat="user in users">
       <ul ng-controller="Albums" ng-init="getAlbumList(user.name)">
          <li ng-repeat="album in albums">
           {{album.title}}
          </li>
       </ul>
     <li>
</ul>


.controller('Albums', ['$scope','Imgur', function($scope, Imgur) {
    $scope.getAlbumList = function(user) {
        Imgur.albumList.get({user:user},    
          function(value) {
                $scope.albums = value.data;
                console.log('success');
          },
          function(error) {
              console.log('something went wrong');
          }
      );
    }}]);

Try this. If not then check this question Reusable components in AngularJS

5 Comments

Also if possible return the entire data set in one server call when your main controller loads into an object graph that you can later traverse without making additional calls.
When I console.log right after it steps into $scope.getAlbumList nothing logs out on page load using ng-init. I read your other post on reusable components. What are the disadvantages of using directives rather than factorys for something like this?
I made some further changes. Also Directives are used to create reusable "widget" or add interactivity or modify DOM elements. Factory is used to create services which encapsulate business logic or make server calls.
That update worked. Could you be so kind to provide another example under this one using the same context into something reusable :) ? And does that method still support two way binding?
OK I will do that sometime later today. I highly recommend you read the Directive documentation docs.angularjs.org/guide/directive

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.