在指令中访问父级作用域

3

我有这两个指令,一个嵌套在另一个指令中:

<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的作用域,但我想知道是否有更好的方法。
5个回答

4

针对这种内部指令通信,我建议在你的 envato 指令上定义一个API/方法,以便你的items指令可以调用。

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

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

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

这种方法的好处在于,即使你将来决定在指令中使用隔离作用域,它也能够正常工作。
AngularJS首页上的“标签页(tabs)”和“面板(pane)”指令都是采用这种通信机制。更多信息请参见https://dev59.com/7Ggu5IYBdhLWcg3wEzDP#14168699,同时也可以查看John的指令与指令之间的通信视频

1

嗯,看起来你在让自己变得更加困难。在你的指令中,你没有设置一个作用域属性:

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

设置 scope: {} 将为您的指令提供一个完全隔离的新作用域。

但是,设置 scope: true 将为您的指令提供一个完全隔离的新作用域,该作用域继承自父级。

我使用此方法将模型包含在顶级父指令中,并允许其通过所有子指令进行过滤。


1

我可以给它设置新的值吗? - Roland
是的,您可以评估任何有效的Angular表达式。您甚至可以传递本地值:scope.$eval('scopeValue + localValue', {localValue: 15})。请注意,在赋值时,子作用域将其值更改,而不是父作用域。在这种情况下,您应该在父级使用一个函数来允许您更改该值(即scope.$eval('changeValue(12345)'))。在此处查看有关作用域继承的更多信息。阅读ng-repeat文档。 - Caio Cunha
我明白了,所以如果我在父指令envato中观察count属性:scope.$watch("count", function(old, current, s) { console.log(old, current, s); });;那么监视器会看到变化,对吗?实际上,count只是一个例子,我只需要将该属性从false设置为true,以便继续执行另一个操作(或任何值告诉我可以继续应用需要应用的函数,此函数需要在将模板附加到DOM /渲染后应用)。在这种情况下,使用$eval更改的正确表达式是什么? - Roland
我刚试过 scope.$eval('swipe = true');,但是这个语句只是在我的 items 作用域中添加了一个 swipe 属性,而没有在父作用域(即 envato 作用域)中添加。 - Roland
使用$apply()怎么样?我读过它比$eval更受欢迎。从你的评论中,我确实得到了一个好东西,使用scope.$last将为我提供一个真值,如果我在最后一次迭代上(这非常有用,因为我刚刚使用了另一种方法来找出它)。但是,我无法理解“使用父级函数允许您更改值(即scope.$eval('changeValue(12345)'))”部分,我需要做什么? - Roland
正如您所看到的,没有运行 $watch,因为更改仅在子级中。关于 $apply,它在内部使用 $eval 并发出 $digest cycle。如果已经在 $digest 中(例如 ng-click$watch),则无法使用它。在父作用域中,您创建一个函数 parentScope.iterate = function() { parentScope.count++; }。在子级中,您调用 $eval('iterate()')。这样,您将迭代父级的属性。 - Caio Cunha

1
我认为您正在寻找一个回调函数,当ng-repeat完成时被调用。如果是这样,我已经创建了一个例子。http://jsfiddle.net/wjFZR/
在例子中没有太多的UI。请打开firebug控制台,并再次运行该例子。您会看到一个日志。该日志在cell指令中定义的ng-repeat结束时被调用。 $scope.rowDone = function(){ console.log($scope) } 这是在行指令上定义的回调函数,它将在cell指令的ng-repeat完成时被调用。
它是这样注册的。 <cell ng-repeat="data in rowData" repeat-done="rowDone()"></cell> 免责声明:我也是angularjs的新手。

我实际上通过@CaioToOn的建议和一些研究找到了一种方法。我可以从“items”指令中执行它,我只需要检查何时scope.$last已经切换为true(这意味着我刚刚循环遍历了对象中的最后一个项目)。但是你的例子是我想到的第一件事,但我想将其与控制器分开,并将其保留在指令本身中 :) - Roland

0

我喜欢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创建单页应用程序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接