AngularJS use a function in a controller to return data from a service to be used in ng-repeat

Jazzy picture Jazzy · Jan 6, 2014 · Viewed 61.5k times · Source

I am trying to use a function in a controller to return data so that I can re-use the function across the app to call the data as well as refresh it occasionally.

My code appears correct, but in practice, crashes the browser on each load.

I am able to get it to work by using an E directive and then placing the element in the partial, but that doesn't accomplish what I need.

Ultimately, I would like to do something like this:

<div ng-repeat="user in getListUsers()">{{ user.somedata }}</div>

I have tried various things to get it to work in testing, including putting the $http in the controller, all with the same results.

If I assign a variable in the controller method instead of returning the data, that works, but then I would need to call the method in the controller and I don't want it to run by default. Just looking to make the call in the templates when needed.

Any help in figuring out why this is not working would be great. Thanks.

Here is what I have that is not functioning...

elite.controller('UserController', function($scope, User){

    $scope.getListUsers = function(){
        User.listUsers().then(function(response){
            return response.users;
        });
    }

});

elite.factory('User', function($http){
   var User = {
      getUser: function(id){
          return $http.get('/user/' + id + '/settings').then(function(response){
              return response.data; 
          });
      },
      listUsers: function(){
        return $http.get('/user/').then(function(response){
          return response.data;
        });
      }
   }
   return User;
});

Edit:

Using some of the ideas below, I am able to get the functionality I wanted, just in a different way. Basically, I want the data to load on demand, rather that up front, as I am trying to group all User logic in that controller and I don't want the full data to load for every user. I am using $rootScope temporarily until I figure out some scoping inheritance issues, as using $scope.users ends up with a null value in the template. I can see the AJAX call coming back, but not sure where it's going, but that's another issue.

elite.controller('UserController', function($scope, $rootScope, User){

    $scope.getListUsers = function(){

        User.listUsers().then(function(response){
            $rootScope.users = response.users;
        });
    };

});

And in a nav element I have this:

<div ng-controller="UserController">
<a ng-href="/#user/list" ng-click="getListUsers()">user list</a>
</div>

Answer

David Lin picture David Lin · Jan 6, 2014

This is about $digest, $watch and ng-repeat. You can find a lot of answers by simply googling it or check out link given by Jsplaine.

In short, your getListUsers() function returns a list of Users every time it is called. However, every returned list is a new collection ( array ). To angular, this means it has to update the display of the list on the page. After each update, Angular actually check a few times to see if the list becomes stable or not by calling getListUsers() function again. Every time angular found that the list is different, so it keep updating the list and keep calling the getListUser function.

To solve this problem, you can create a variable holding the user list:

 $scope.userList = [];

and also the function to update the list

$scope.reloadListUsers = function(){
    User.listUsers().then(function(response){
        $scope.userList = response.users;
    });
}

Then in the ng-repeat directive

<div ng-repeat="user in userList">{{ user.somedata }}</div>

Where you want to reload the list, simple call the reloadListUsers function .

<button ng-click="reloadListUsers()" > reload </button>

The reason behind is that now the variable userList is used in the binding instead of the function call expression, and it becomes stable once the list is loaded.