2

The call directive function of the child directive does not work

 2 years ago
source link: https://www.codesd.com/item/the-call-directive-function-of-the-child-directive-does-not-work.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The call directive function of the child directive does not work

advertisements

I am trying to use a directive created by somebody else to tell my directive when the ngRepeat inside it has finished creating itself.

My outer directive has an isolate scope and has a scope function which needs to be called by the inner directive. Here is the code.

angular.module('my.directives')

.controller('myTableCtrl', function($scope, $element, $attrs) {

    $scope.tableLoaded = function(tableName){
        console.log("Table Loaded");
    };

})

.directive('myTable', function() {
    return {
        restrict: 'EA',
        scope: {},
        controller: 'myTableCtrl as myTable'
    };
})

.directive('repeatDone', function($timeout) {
    return function(scope, element, attrs) {
        if (scope.$last) {
            $timeout(function(){
                scope.$eval(attrs.repeatDone);
            });
        }
    }
});

My HTML looks like this.

<my-table>
    <div ng-repeat="row in tableRows" repeat-done="tableLoaded('main');"></div>
</my-table>

Before I added the scope:{} to the myTable directive it was getting very confused when there were multiple tables on the page (the tableLoaded function was getting called but by the wrong child), so I added the isolate scope which I think is best practice. Unfortunately the repeatDone directive is now unable to see/call the tableLoaded() function in the parent myTable directive.

Any help would be really appreciated.

EDIT:

Just to clarify. tableLoaded() is a function of the myTable directive and I want to be able to call it from the repeatDone directive which could be however may elements deep inside the table directive. In addition to this I don't want to change the repeatDone directive and repeatDone wouldn't require or even be aware of the myTable directive.


Please look at the following solution. Hope this helps :)

    angular.module('my.directives')

    .controller('myTableCtrl', function($scope, $element, $attrs) {

    this.tableLoaded = function(tableName){    //bind the function to controller rather than $scope
        console.log("Table Loaded");
    };

    })

    .directive('myTable', function() {
    return {
        restrict: 'EA',
        scope: {},
        controller: 'myTableCtrl as myTable'
    };
})

.directive('repeatDone', function($timeout) {
    return {
        restrict: 'EA',
        scope: true,  //your child directive might want to inherit stuff from the parent directive's controller maybe
        require:'?^myTable',  //require the parent directive
        link: function(scope, element, attrs,tableCtrl) {  //the controller of myTable is available as 4th param in link function (because of require)
            if (scope.$last) {
                $timeout(function(){
                    //scope.$eval(attrs.repeatDone);
                    tableCtrl.tableLoaded();    //call the function of the parent's directive (the specific instance)
                });
            }
        }
    }
});

Edit:

The issue I'm thinking of is the possibility of having multiple tables at once each having one repeatDone inside. What you can do is, you can Emit an event on completion up the scope and catch it in your required controller/directive. Optionally, you can pass a unique identifier too so it catches only inside a particular controller/directive. Here's the example:

.directive('repeatDone', function($timeout) {
    return {
        restrict: 'EA',
        link: function(scope, element, attrs) {
          console.log('in directive');
            var targetUID = attrs.target;
          console.log(targetUID);
            if (scope.$last) {
                $timeout(function(){

                  console.log('repeat-done now');//scope.$eval(attrs.repeatDone);
                    scope.$emit('repeat-done-' + targetUID,{});
                });
            }
        }
    };
});

in your table directive:

.directive('myTable', function() {
    return {
        restrict: 'EA',
        controller: 'myTableCtrl as myTable'
    };
})

and in your table controller:

.controller('myTableCtrl', function($scope, $element, $attrs) {
    console.log('in table Ctrl');
            var self = this;
            $scope.tableUID = $attrs.tableId;
            self.tableLoaded = function(){
                console.log("Table Loaded");
            };
            console.log('tableUID' + $scope.tableUID);
            $scope.$on('repeat-done-' + $scope.tableUID, function(event,args){
              console.log('in catcher function');
                self.tableLoaded();
            });
})

then use

<my-table table-id="table1">
    <div ng-repeat="row in tableRows" repeat-done target="table1"></div>
</my-table>

Here's a working JSBIN

Hope this helps


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK