1

I'm using this app.js file:

var myNinjaApp = angular.module('myNinjaApp', []);

myNinjaApp.directive('randomNinja', [function(){
  return {
    restrict: 'E',
    scope: {
      theNinjas: '=',
      title: '='
    },
    template: '<img ng-src="{{theNinjas[random].thumb}}"/>',
    controller: function($scope) {
      let length = $scope.theNinjas.length; //$scope.theNinjas is undefined
      $scope.random = Math.floor(Math.random() * length);
      console.log($scope.random);
    }
  };
}]);

myNinjaApp.controller('NinjaController', ['$scope', '$http', function($scope, $http) {  
    $http.get('data/ninjas.json').then(function(data){
      $scope.ninjas = data.data;
    });
}]);

and this index.html file:

<!DOCTYPE html>
<html lang="en" ng-app="myNinjaApp">
  <head>
    <title>TheNetNinja Angular Playlist</title>
    <link href="content/css/styles.css" rel="stylesheet" type="text/css" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-route/1.7.9/angular-route.min.js"></script>
    <script src="app\app.js"></script>
  </head>
  <body ng-controller="NinjaController">  
    <random-ninja the-ninjas="ninjas" title="'Random Ninja'"></random-ninja>
  </body>
</html>

I noticed in the directive's controller code, the $scope.theNinjas is undefined, but the title isn't.

When I debug the code, I can see the $http.get() call is made after the $scope.theNinjas.length is read.

How can I get the length of the array in the directive's controller?

David Klempfner
  • 8,700
  • 20
  • 73
  • 153

2 Answers2

1

Your problem is, $http.get is async,

$http.get('data/ninjas.json').then(function(data){
   $scope.ninjas = data.data;
});

Therefore, you get a response a.e. initialize $scope.ninjas with delay.

You can go on 2 solutions:

[Solution 1]

Use ng-if to initialize the directive when ninjas is not undefined

<random-ninja 
  ng-if="ninjas"
  the-ninjas="ninjas" 
  title="'Random Ninja'">
</random-ninja>

[Solution 2]

Use $watch to listen on ninjas change and call some run method when $scope.theNinjas has value different from undefined. After, stop watch by calling unreg()

let length = 0;

var unreg = $scope.$watch(function () {
    return $scope.theNinjas;
},
function (newVal, oldVal) {
   if (newVal !== undefined) {
        run(newVal);
        unreg();
    }
 });


function run(){
 length = $scope.theNinjas.length; 
  $scope.random = Math.floor(Math.random() * length);
  console.log($scope.random);
}
Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
1

Another approach is to use the $onChanges life-cycle hook:

myNinjaApp.directive('randomNinja', [function(){
  return {
    restrict: 'E',
    scope: {
      ̶t̶h̶e̶N̶i̶n̶j̶a̶s̶:̶ ̶'̶=̶'̶,̶ 
      theNinjas: '<',
      title: '<'
    },
    template: '<img ng-src="{{theNinjas[random].thumb}}"/>',
    controller: function($scope) {
      this.$onChanges = function(changesObj) {
        if (changesObj.theNinjas) {
          let length = changesObj.theNinjas.currentValue.length;
          $scope.random = Math.floor(Math.random() * length);
          console.log($scope.random);
        };
      };
    }
  };
}]);

This will update the component directive when the theNinjas binding is updated with data from the server.

Note: The directive needs to use one-way (<) binding for this approach.

From the Docs:

Life-cycle hooks

Directive controllers can provide the following methods that are called by AngularJS at points in the life-cycle of the directive:

  • $onChanges(changesObj) - Called whenever one-way (<) or interpolation (@) bindings are updated. The changesObj is a hash whose keys are the names of the bound properties that have changed, and the values are an object of the form { currentValue, previousValue, isFirstChange() }. Use this hook to trigger updates within a component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will also be called when your bindings are initialized.

For more information, see

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95