Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJs: Why does scope inside my directive not update when async data arrives?

I have a directive with isolate scope which takes a scope variable by reference

angular.module('myApp')
    .directive('myDirective', function() {
        return {
            scope: {
                items: '='
            },
            templateUrl: 'template.html',
            replace: true,
            controller: 'myDirectiveCtrl',
            controllerAs: 'ctrl'
        };
    })
    .controller('myDirectiveCtrl', function($scope) {
        this.items = $scope.items;
    });

This is passed in like so:

    <div my-directive items='items'></div>

In the external controller data is asynchronously loaded and the scope items passed into the directive updated:

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

    $scope.setItems = function() {
      $scope.items = [
        'Here',
        'There',
        'Everywhere'
      ];
    };
  });

When the data is loaded, the scope outside my directive updates, but inside it doesn't

My html:

      <div my-directive items='items'></div> <!-- this doesn't update --> 

      Outside directive
      <ul ng-repeat='i in items'>            <!-- this does update -->
        <li>{{i}}</lu>
      </ul>

      <button ng-click="setItems()">Set items</button>

How can I get my scope inside my directive to update? Do I

Plunker here

like image 601
Steve Lorimer Avatar asked Nov 14 '14 23:11

Steve Lorimer


1 Answers

When Angular first runs your directive's controller function, your $scope.items === undefined, so when you do this.items = $scope.items, your this.items === undefined too.

That's it. After that there is nothing that changes this.items.

This is unlike $scope.items. $scope.items is two-way bound to the outer scope, so whenever Angular detects a change externally, it sets the isolated scope variable.

The easiest way (and most suitable, in my opinion) is to use the $scope property directly in the directive:

<div>
    Inside directive
    <ul ng-repeat="i in items">
      <li>{{ i }}</li>
    </ul>
</div>

If you want to use your controller as ViewModel instead of scope (I don't know why you would), you could do:

$scope.$watchCollection("items", function(newVal, oldVal) {
   ctrl.items = newVal;
});

EDIT:

In Angular 1.3 you can also do bindToController: true in the directive's definition, so that the controller property "items" will get the two-way binding that $scope.items gets. Then, you don't even need to do this.items = $scope.items;:

Your forked plunker to illustrate.

like image 109
New Dev Avatar answered Oct 11 '22 15:10

New Dev