【问题标题】:accessing parent scope inside a directive访问指令内的父作用域
【发布时间】:2013-03-03 15:11:52
【问题描述】:

我有这两个指令,一个相互嵌套:

<envato class="container content-view-container" data-ng-cloak data-ng-hide="spinner">
    <items data-ng-repeat="items in marketplaces"></items>
</envato>

这两个都是这样定义的:

Application.Envato.directive("envato", ["$timeout", function($timeout){

    var object = {

        restrict : "E",
        controller : "EnvatoAPIController",
        transclude : true,
        replace : true,
        templateUrl : "templates/envato-view.php",
        link : function(scope, element, attrs, controller) {

            console.log(scope);

            return controller.getLatestItems().then(function(data) {



                scope.marketplaces = angular.fromJson(data);
                scope.count = scope.marketplaces.length;

                var tst = angular.element(element).find(".thumbnails");

                /* $timeout(function() { scope.swiper = new Swipe(document.getElementById('swiper-container')); }, 5000); */                    
                scope.spinner = false;
            });

        }
    };

    return object;
}]);

Application.Envato.directive("items", function(){

    var iterator = [],
        object = {

            require : "^envato",
            restrict : "E",
            transclude : false,
            replace : true,
            templateUrl : "templates/envato-items-view.php",
            link : function(scope, element, attrs, controller) {

                iterator.push(element);

                if (iterator.length === scope.$parent.$parent.count) { console.log(iterator); };                                    
            }
        };

    return object;
});

上面的很多代码可能没有多大意义,因为它是更大应用程序的一部分,但我希望它可以解决我的问题。我要做的是从指令items 更改指令envato 的范围属性。因为我有一个迭代,我想知道它什么时候完成,所以我可以在迭代期间对附加的 DOM 元素执行另一个操作。

例如,假设我将在指令envato 中定义scope.swipe,并观察它的变化。在指令items 中,我将观察ng-repeat 何时完成,然后更改上面定义的范围属性scope.swipe。这将触发指令envato 内部的更改,现在我知道我可以做我的操作了。

我希望我足够清楚,如果不是,我可以尝试编写更多代码,或者我会尝试更具体。我怎样才能实现我刚才描述的?

编辑:我知道在指令items 中使用:console.log(angular.element(element.parent()).scope()); 会给我envato 指令的范围,但我想知道是否有更好的方法这样做。

【问题讨论】:

    标签: javascript angularjs angularjs-directive angularjs-scope


    【解决方案1】:

    对于这种指令间通信,我建议在您的envato 指令上定义一个您的items 指令可以调用的API/方法。

    var EnvatoAPIController = function($scope) {
        ...
        this.doSomething = function() { ... }
    }
    

    您的items 指令已经requires envato 指令,因此在您的items 指令的链接函数中,只需在适当的时候调用API:

    require : "^envato",
    link : function(scope, element, attrs, EnvatoCtrl) {
       ...
       if(scope.$last) {
           EnvatoCtrl.doSomething();
       }
    }
    

    这种方法的好处在于,即使你有一天决定在指令中使用隔离作用域,它也可以工作。

    AngularJS 主页上的tabspane 指令使用这种通信机制。请参阅https://stackoverflow.com/a/14168699/215945 了解更多信息。另请参阅 John 的 Directive to Directive Communication video

    【讨论】:

      【解决方案2】:

      item 指令中使用scope.$eval('count') 并让角度为您解析。

      【讨论】:

      • 我可以对其应用新值吗?
      • 是的,您可以评估任何valid angular expression。您甚至可以传递本地值:scope.$eval('scopeValue + localValue', {localValue: 15})。请注意,在分配时,子范围将更改其值,而不是父范围。在这种情况下,您应该使用父级的函数来允许您更改值(即scope.$eval('changeValue(12345)'))。查看here 了解有关范围继承的更多信息。阅读 ng-repeat 文档。
      • 我明白了,所以如果我在父指令envato 中注意count 属性:scope.$watch("count", function(old, current, s) { console.log(old, current, s); });;手表会看到变化吗?实际上,count 只是一个示例,我只会将属性从 false 设置为 true 以便继续执行另一个操作(或任何告诉我可以继续应用需要应用的功能的值将模板附加到 DOM / 中呈现)。在这种情况下,用$eval 改变它的正确表达是什么?
      • 我刚刚尝试过scope.$eval('swipe = true');,但这个只是将属性swipe 添加到我的items 作用域而不是父作用域(envato 作用域)。
      • 使用$apply() 怎么样,我听说它比$eval 更受欢迎。我确实从您的评论中得到了一件好事,如果我在最后一次迭代中,使用scope.$last 会给我一个真实的结果(这非常有用,因为我只是使用了另一种方法来找出它)。但我无法获得use a function at parent to allow you to change the value (i.e. scope.$eval('changeValue(12345)')). 部分,我必须在那里做什么?
      【解决方案3】:

      我认为您正在寻找在 ng-repeat 完成时调用的回调。如果这就是你想要的,我已经创建了一个小提琴。 http://jsfiddle.net/wjFZR/.

      小提琴中没有太多的 UI。请打开萤火虫控制台,然后再次运行小提琴。您将看到一个日志。该日志在cell 指令中定义的ng-repeat 末尾调用。

      $scope.rowDone = function(){ console.log($scope) } 这是在行指令上定义的回调函数,当单元指令的ng-repeat 完成时将被调用。

      就是这样注册的。

      &lt;cell ng-repeat="data in rowData" repeat-done="rowDone()"&gt;&lt;/cell&gt;

      免责声明:我也是 angularjs 的新手。

      【讨论】:

      • 我实际上通过@CaioToOn 的建议和一些研究找到了一种方法。我可以从items 指令中做到这一点,我只需要检查scope.$last 何时切换为true(这意味着我刚刚循环了对象中的最后一项)。但是你的例子是我的第一件事,但我想让它远离控制器并将它保存在它自己的指令中:)
      【解决方案4】:

      嗯,看来您是在试图让自己变得困难。在您的指令中,您没有设置范围属性:

      var object = {
          restrict : "E",
          transclude : true,
          replace : true,
          scope: true,
          ...
      

      设置范围:{} 将为您的指令提供一个完全隔离的新范围。

      BUT 设置范围:true 将为您的指令提供一个完全隔离的新范围,该范围继承父级。

      我使用这种方法将模型包含在顶级父指令中,并允许它向下过滤所有子指令。

      【讨论】:

        【解决方案5】:

        我喜欢 Mark 的回答,但我最终创建了一个属性指令来将元素指令的范围保存到 rootScope,如下所示:

        myApp.directive('gScope', function(){
            return {
                restrict: 'A',
                replace: false,
                transclude: false,
                controller: "DirectiveCntl",
                link: function(scope, element, attrs, controller) {
                    controller.saveScope(attrs.gScope);
                }
            }
        });
        
        ...
        
        function DirectiveCntl($scope, $rootScope) {
            this.saveScope = function(id) {
                if($rootScope.directiveScope == undefined) {
                    $rootScope.directiveScope = [];
                }
                $rootScope.directiveScope[id] = $scope;
            };
        }
        
        ...
        
        <span>Now I can access the message here: {{directiveScope['myScopeId'].message}}</span>
        <other-directive>
            <other-directive g-scope="myScopeId" ng-model="message"></other-directive>
        </other-directive>
        

        注意:虽然这使得从所有各种指令中收集数据变得很容易,但我提醒您,现在您必须确保正确管理潜在的范围堆,以避免导致页面上的内存泄漏。特别是如果您使用 ng-view 创建单页应用程序。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-11-27
          • 1970-01-01
          • 2020-04-22
          • 1970-01-01
          • 2017-10-15
          • 2013-05-22
          • 2016-01-25
          相关资源
          最近更新 更多