Nick
Nick

Reputation: 14283

How to get the length of an array without ngRepeat

I'm trying to count the items in an array without using ng-repeat (I don't really need it, i just want to print out the sum).

This is what I've done so far: http://codepen.io/nickimola/pen/zqwOMN?editors=1010

HTML:

<body ng-app="myApp" ng-controller="myCtrl">
    <h1>Test</h1> 
    <div ng-cloak>{{totalErrors()}}</div>
</body>

Javascript:

angular.module('myApp', []).controller('myCtrl', ['$scope', '$timeout', function($scope) {

    $scope.tiles= {
            'data':[
                {'issues':[
                    {'name':'Test','errors':[
                        {'id':1,'level':2},
                        {'id':3,'level':1},
                        {'id':5,'level':1},
                        {'id':5,'level':1}
                    ]},
                    {'name':'Test','errors':[
                        {'id':1,'level':2,'details':{}},
                        {'id':5,'level':1}
                    ]}
                ]}
            ]}
  $scope.totalErrors = function() {
  if ($scope.tiles){
        var topLevel = $scope.tiles.data
        console.log (topLevel);
   return topLevel[0].issues.map(function(o) {
            return o.errors.length
          })
          .reduce(function (prev, curr){
            return prev + curr
          })
    }
    }
}]);

This code works on codepen, but on my app I get this error:

Cannot read property '0' of undefined

and if I debug it, topLevel is undefined when the functions is called.

I think it is related to the loading of the data, as on my app I have a service that looks like this:

angular.module('services', ['ngResource']).factory('tilesData', [
  '$http', '$stateParams', function($http, $stateParams) {
    var tilesData;
    tilesData = function(myData) {
      if (myData) {
        return this.setData(myData);
      }
    };
    tilesData.prototype = {
      setData: function(myData) {
        return angular.extend(this, myData);
      },
      load: function(id) {
        var scope;
        scope = this;
        return $http.get('default-system.json').success(function(myData) {
          return scope.setData(myData.data);
        }).error(function(err) {
          return console.error(err);
        });
      }
    };
    return tilesData;
  }
]);

and I load the data like this in my controller:

angular.module('myController', ['services', 'ionic']).controller('uiSettings', [
  '$scope', '$ionicPopup', '$ionicModal', 'tilesData', function($scope, $ionicPopup, $ionicModal, tilesData) {
    $scope.tiles = new tilesData();
    $scope.tiles.load();
    $scope.totalErrors = function() {
      debugger;
      var topLevel;
      topLevel = $scope.tiles.data;
      console.log(topLevel);
      return topLevel[0].issues.map(function(o) {
        return o.errors.length;
      }).reduce(function(prev, curr) {
        return prev + curr;
      });
    };
  }
]);

but I don't know what to do to solve this issue. Any help will be really appreciated. Thanks a lot

Upvotes: 0

Views: 647

Answers (1)

dafyddPrys
dafyddPrys

Reputation: 918

The $http.get() method is asynchronous, so you can handle this in your controller with a callback or a promise. I have an example using a promise here.

I've made an example pen that passes back the sample data you use above asynchronously.This mocks the $http.get call you make.

I have handled the async call in the controller in a slightly different way to what you had done, but this way it works with the .then() pattern that promises use. This should give you an example of how you can handle the async code in your controller.

Note as well that my service is in the same module as my controller. This shouldn't matter and the way you've done it, injecting your factory module into your main module is fine.

angular.module('myApp', [])

//Define your controller
.controller('myCtrl', ['$scope','myFactory', function($scope,myFactory) {        
    //call async function from service, with .then pattern:
    myFactory.myFunction().then(
    function(data){
        // Call function that does your map reduce
        $scope.totalErrors = setTotalErrors();
    },
    function(error){
        console.log(error);
    });

  function setTotalErrors () {
    if ($scope.tiles){
                var topLevel = $scope.tiles.data
                console.log (topLevel);
     return topLevel[0].issues.map(function(o) {
                        return o.errors.length
                    })
                    .reduce(function (prev, curr){
                        return prev + curr
                    });
        }
 }
}])
.factory('myFactory', ['$timeout','$q',function($timeout,$q){
return {
    myFunction : myFunction
};

function myFunction(){
    //Create deferred object with $q.
    var deferred = $q.defer();
    //mock an async call with a timeout
    $timeout(function(){
        //resolve the promise with the sample data
        deferred.resolve(
            {'data':[
            {'issues':[
                {'name':'Test','errors':[
                    {'id':1,'level':2},
                    {'id':3,'level':1},
                    {'id':5,'level':1},
                    {'id':5,'level':1}
                ]},
                {'name':'Test','errors':[
                    {'id':1,'level':2,'details':{}},
                    {'id':5,'level':1}
                ]}
            ]}
        ]})
    },200);

    //return promise object.
    return deferred.promise;
}
}]);

Have a look : Link to codepen

Also, have a read of the $q documentation: documentation

Upvotes: 2

Related Questions