2

How to use an isolated scope property properly?

I have a directive, that is called from a page controller, with an attribute item passed to it, e.g. <my-directive item="myItem"></my-directive>, containing an id.

The code below will not work, as it seems like $scope.item is undefined in the controller.. like I'm using it too soon. How can I be sure that it is actually set when I want to use it?

app.directive('myDirective', [function() {
return {
    restrict: 'E',
    templateUrl: 'template.html',
    scope: {
        item: "="
    },
    controller: ['$scope', 'ExtendedItemFactory', function($scope, ExtendedItemFactory) {
        this.extendedInfo = ExtendedItemFactory.get({ id: $scope.item.id });
    }],
    controllerAs: 'MyDirectiveCtrl'
};
}]);
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
Andreas
  • 1,211
  • 1
  • 10
  • 21

3 Answers3

1

You could use $watch inside your directive that will watch on change in value & will fire the code which you want.

Code

app.directive('myDirective', [function() {
    return {
        restrict: 'E',
        templateUrl: 'template.html',
        scope: {
            item: "="
        },
        controller: ['$scope', 'ExtendedItemFactory', function($scope, ExtendedItemFactory) {
            this.extendedInfo = ExtendedItemFactory.get({
                id: $scope.item.id
            });
            $scope.$watch('item', function(newVal, oldVal) {
                if (newVal && newVal != oldVal)
                    this.extendedInfo = ExtendedItemFactory.get({
                        id: $scope.item.id
                    });
            }, true).bind(this);
        }],
        controllerAs: 'MyDirectiveCtrl'
    };
}]);
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • 1
    I have to say that it is strange that angular does not provide a simpler native solution for this. – Andreas Jun 11 '15 at 06:26
  • @Andreas yes it would work without `.bind(this)` because the we are accessing isolatated scope variable directly..if we wanted to use scope variables of link to the directive controller then only we would use `.bind(this)`.. do up-vote if it does helped you..that.. – Pankaj Parkar Jun 11 '15 at 07:54
0

You're using controllerAs, so you shouldn't need to inject $scope in this instance.

I would change your directive definition to the following, noting the use of bindToController, which will ensure that your isolated scope values are populated and available on your controller:

app.directive('myDirective', [function() {
    return {
        restrict: 'E',
        templateUrl: 'template.html',
        scope: {
            item: "="
        },
        controller: ['ExtendedItemFactory', function(ExtendedItemFactory) {
            this.extendedInfo = ExtendedItemFactory.get({ id: this.item.id });
        }],
        controllerAs: 'MyDirectiveCtrl',
        bindToController: true
    };
}]);
Nat Wallbank
  • 1,377
  • 12
  • 12
  • This seems not to work, `item` is not yet defined in the controller when trying your code. – Andreas Jun 11 '15 at 06:22
  • `` where `myItem` is a scope variable on a page controller. – Andreas Jun 11 '15 at 08:25
  • I've used this pattern loads of times without problems, and I can't immediately see anything wrong from the code snippets shown. Can you reproduce in a plunker or something? – Nat Wallbank Jun 11 '15 at 08:55
0

Instead of initializing extendedInfo when the directive loads you could create getter function that retrieves it on demand.

this.getExtendedInfo = function(){
    return ExtendedItemFactory.get({ id: $scope.item.id });
}

Alternatively you prevent your directive from loading until the item is ready

<div ng-if="ctrl.item">
    <my-directive item="ctrl.item"></my-directive>
</div>
rob
  • 17,995
  • 12
  • 69
  • 94
  • I must load the information directly on load, so your first suggestion does not work. Your second suggestion is an interesting approach, but it feels wrong to put the logic outside of the directive itself... – Andreas Jun 11 '15 at 06:24