How to use an isolated scope property properly?

83 views Asked by At

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'
};
}]);
3

There are 3 answers

2
Pankaj Parkar On BEST ANSWER

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'
    };
}]);
3
Nat Wallbank On

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
    };
}]);
1
rob On

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>