【问题标题】:Calling directive function from child directive not working从子指令调用指令函数不起作用
【发布时间】:2016-03-13 21:29:56
【问题描述】:

我正在尝试使用其他人创建的指令来告诉我的指令什么时候它里面的 ngRepeat 完成了自己的创建。

我的外部指令有一个隔离范围,并且有一个需要由内部指令调用的范围函数。这是代码。

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);
            });
        }
    }
});

我的 HTML 看起来像这样。

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

在我将 scope:{} 添加到 myTable 指令之前,当页面上有多个表时会变得非常混乱(tableLoaded 函数被调用但被错误的孩子调用),所以我添加了隔离范围,我认为是最佳实践。不幸的是,repeatDone 指令现在无法查看/调用父 myTable 指令中的 tableLoaded() 函数。

任何帮助将不胜感激。

编辑:

只是为了澄清。 tableLoaded() 是 myTable 指令的一个函数,我希望能够从 repeatDone 指令中调用它,但它可能是 table 指令深处的元素。除此之外,我不想更改 repeatDone 指令,并且 repeatDone 不需要甚至不知道 myTable 指令。

【问题讨论】:

  • tableRows 在哪里定义?它不在您发布的任何源代码中。
  • 在我的主页控制器中。此外,我将表格行模板简化为一个 div 以使其更易于阅读。这肯定是有效的,因为当我从 myTable 中删除隔离范围时,表中充满了数据,并且 tableLoaded() 函数被正确调用。
  • @jonhobbs 为什么不使用隔离作用域并将您的函数与“&”连接到内部指令?
  • 不确定你的意思,你的意思是修改这个特定场景的repeatDone指令吗?如果是这样,那么这不是一个真正的选择。如果您的意思是有一种方法可以更改 table 指令,以便嵌套的 repeatDone 可以在其范围内调用函数,那么这正是我想要做的。

标签: javascript angularjs angularjs-directive angularjs-scope


【解决方案1】:

请查看以下解决方案。希望这会有所帮助:)

    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)
                });
            }
        }
    }
});

编辑:

我正在考虑的问题是一次拥有多个表的可能性,每个表内部都有一个 repeatDone。 您可以做的是,您可以在完成范围时发出一个事件并将其捕获到您所需的控制器/指令中。或者,您也可以传递一个唯一标识符,以便它仅在特定控制器/指令内捕获。 示例如下:

.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,{});
                });
            }
        }
    };
});

在你的表格指令中:

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

在你的表控制器中:

.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();
            });
})

然后使用

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

这是一个有效的JSBIN

希望对你有帮助

【讨论】:

  • 谢谢Ahsan,但repeat-done 应该是一个通用指令,可用于任何repeat 以调用作用域链更高位置的函数。它不应该知道 myTable
  • 您能否确认它可以是“范围链的更高位置”或只是直接父范围?
  • repeat-done 可以是我的表深处的任意数量的范围,但只有 table 指令将具有 tableLoaded() 函数。
  • @jonhobbs 使用工作示例链接编辑了我的回复,请检查
  • 感谢 Ahsan,指令耦合和发射事件是我想要避免的两件事。我认为这可以通过使用范围链来完成,因为 repeatDone 是表的子项,但经过一些研究后,似乎隔离范围不仅将自己与父母隔离,而且将自己完全从范围链中删除.所以看来你的建议是我唯一的两个选择。非常感谢。
【解决方案2】:

我认为您可能需要在 my-table 上使用 ng-transclude 才能使用内部元素

【讨论】:

  • 这不起作用,因为我没有将模板应用于指令,因此它无处可嵌入。
  • 你也在使用控制器。您不应该将您的方法绑定到this 而不是$scope 吗?
  • 我不确定。通常我的方法都在作用域上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-13
  • 2017-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 2015-11-24
相关资源
最近更新 更多