如何使兄弟指令进行通信工作(某些特定指令之间的通信)

时间:2015-06-09 17:41:43

标签: angularjs angularjs-directive

所有

假设我有两个指令(dir1和dir2),它们都是隔离的范围。从一些帖子中,我了解到我需要使用"要求"获得另一个指令的范围,但有一个问题让我很困惑:

假设我使用ng-repeat生成了很多dir1和dir2,我怎么知道某些dir1,需要哪个dir2的控制器范围(cos有很多dir2,在我的理解中,所有这些范围在dir2控制器中彼此独立)?

例如:

app.directive("dir1", function(){
    var counter = 0;
    return {
        restrict:"AE",
        scope:{},
        template: "<button class='dir1_btn'>highlight dir2_"+(couter++)+" </button>",
        link:function(scope, EL, attrs){
             EL.find("button.dir1_btn").on("click", function(){
                 // How to let according dir2 know this click?
             })
        }
    }
}) 

app.directive("dir2", function(){
    var counter = 0;
    return {
        restrict:"AE",
        scope:{},
        template: "<span class='dir2_area'>This is dir2_"+(couter++)+" </span>",
        link:function(scope, EL, attrs){
             // Maybe put something here to listening the event passed from dir1?
        }
    }
}) 

和html一样(为了简单的目的,我只在其中放了两个,实际上它将由ng-repeat生成):

<dir1></dir1>
<dir2></dir2>
<dir1></dir1>
<dir2></dir2>

考虑到这就像开关和灯一样,dir1是根据灯光(dir2)打开(通过改变背景颜色)的开关。

  

在实际项目中,我想做的是angularJS指令版本   sidemenu和scrollContent,sidemenu中的每个项目都是一个指令,   单击它将根据内容(另一个指令)自动滚动   顶部。

我想知道该怎么做?我知道这在jQuery中很容易,只是想知道如何将它挂钩到AngularJS数据驱动模式中。

由于

2 个答案:

答案 0 :(得分:1)

您可能必须使用某种策略。某种标识符挂钩。很明显,你不能使用require(要求指令的控制器,你也没有任何东西,它只能查看祖先或本身而不是兄弟姐妹)。例如,您可以添加id属性和for属性,并使用基于特定属性值的选择来定位元素并触发事件。这个相关元素的位置并不重要。

你的指令可能如下:

  <dir1 dir-for="id1"></dir1>
  <dir2 dir-id="id1"></dir2>

  <dir1 dir-for="id2"></dir1>
  <dir2 dir-id="id2"></dir2>

简单的实施:

.directive("dir1", function($document) {
  var counter = 0;
  return {
    restrict: "AE",
    scope: {
      dirFor: '@'
    },
    template: "<button class='dir1_btn' ng-click='handleClick()'>highlight dir2_({{dirFor}}) </button>",
    link: function(scope, EL, attrs) {

      var $target = angular.element(
                $document[0].querySelector('[dir-id="' + scope.dirFor + '"]'))
               .contents().scope();

      var clicked = false;

      scope.handleClick = function() {
        clicked = !clicked;
        targetScope.$broadcast("SWITCH_CLICKED", clicked);
      }

      scope.$on('$destory',function() {
        $target = null;
      }
    }
  }
})

app.directive("dir2", function() {
  var counter = 0;
  return {
    restrict: "AE",
    scope: {
      dirId: '@'
    },
    template: "<span class='dir2_area' ng-class=\"{true:'on', false:'off'}[status]\">This is dir2_{{dirId}}</span>",
    link: function(scope, EL, attrs) {
      console.log(scope.$id);
      scope.status = false;
      scope.$on('SWITCH_CLICKED', function(e, data) {

        scope.status = data;
      });
    }
  }
});

<强>演示

&#13;
&#13;
var app = angular.module('app', []).controller('ctrl', angular.noop);

app.directive("dir1", function($document) {
  var counter = 0;
  return {
    restrict: "AE",
    scope: {
      dirFor: '@'
    },
    template: "<button class='dir1_btn' ng-click='handleClick()'>highlight dir2_({{dirFor}}) </button>",
    link: function(scope, EL, attrs) {

      var $target = angular.element($document[0].querySelector('[dir-id="' + scope.dirFor + '"]')).contents();

      var clicked = false;

      scope.handleClick = function() {
        clicked = !clicked;
        $target.scope().$broadcast("SWITCH_CLICKED", clicked);
      }

      scope.$on('$destroy',function() {
        $target = null;
      });
    }
  }
})

app.directive("dir2", function() {
  var counter = 0;
  return {
    restrict: "AE",
    scope: {
      dirId: '@'
    },
    template: "<span class='dir2_area' ng-class=\"{true:'on', false:'off'}[status]\">This is dir2_{{dirId}}</span>",
    link: function(scope, EL, attrs) {
      
      scope.status = false;
      
      scope.$on('SWITCH_CLICKED', function(e, data) {
     
        scope.status = data;
      });
    }
  }
})
&#13;
.on{
color:green;
}
.off{
color:blue;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  <dir1 dir-for="id1"></dir1>
  <dir2 dir-id="id1"></dir2>

  <dir1 dir-for="id2"></dir1>
  <dir2 dir-id="id2"></dir2>

</div>
&#13;
&#13;
&#13;

我已经使用$document[0].querySelector('[dir-id="' + scope.dirFor + '"]')).contents().scope()来获取范围,同样您也可以.controller来获取控制器实例。当前的例子是进行绝对选择(使用文档),你也可以使它相对。

答案 1 :(得分:1)

这里要注意的最重要的事情是我认为你想使用ng-class因为你在ng-repeat中创建了两个指令,我假设你正在迭代一个对象列表(即使它们是两个单独的ng-repeats,如果你遍历同一个对象列表,它将起作用.JQuery不应该是必要的)?将ngClass对象附加到迭代的每个对象,将其放在dir2中的ng-class属性上,然后给dir1访问权限以进行更改。如果要为过渡设置动画,ngClass会提供动画钩子。我的其余部分可能会有所帮助,但我想重做它,因为我想到了ng-class。我现在必须回去工作了。我会留意反馈,如果您有疑问,请尽快回答。

我认为可能有几种方法可以更好地完成你想要做的事情。目前尚不清楚为什么两个指令都需要具有隔离范围。当我使用更多角度时,我发现虽然隔离范围是一种强大的技术,但最好避免过度使用它。

对于require指令属性,此帖explains how to make directives communicate via their controllers非常好。

我有两个可能的建议给你。  制定一个指令 为什么你不能把模板合二为一?

或者,如果我认为他们需要分开的某些原因,你可以考虑在他们之间共享一个对象。

<div ng-repeat='obj in sharedDirObjs'>
  <dir1 shared-dir-obj='obj'></dir1>
  <dir2 shared-dir-obj='obj'></dir2>
</div>

app.controller('ctrl', function() {
  $scope.sharedDirObjs = [obj1, obj2, obj3]
});

app.directive("dir1", function(){
var counter = 0;
return {
    restrict:"AE",
    scope:{sharedDirObj : '='},
    template: "<button class='dir1_btn' ng-click='clickFn()'>highlight dir2_"+(couter++)+" </button>",
    link:function(scope, EL, attrs){
       var dir1vars...     
       scope.clickFn = function(){
             // dir1 logic...
             scope.sharedDirObj.dir2.clickFn(dir1vars...);
       };
    }
}}) 

app.directive("dir2", function(){
    var counter = 0;
    return {
        restrict:"AE",
        scope:{sharedDirObj : '='},
        template: "<span class='dir2_area'>This is dir2_"+(couter++)+" </span>",
        link:function(scope, EL, attrs){
             scope.sharedDirObj.dir2 = {};
             scope.sharedDirObj.dir2.clickFn(dir1vars...) {
                 // access to dir2 vars
             };
        }
    }})

类似地,您可以创建一个服务,该服务包含通过注入服务共享的对象数组,并使用ng-repeat中的$ index索引,或者您可以使用PSL建议的id系统。请注意,我上面描述的解决方案可以使用隔离范围,或者没有使用scope.$eval(attr.sharedObj);在您的任何一个或两个指令上。这个问题的答案provides a solid runthrough of when and why to use isolated scope.在任何情况下,如上所示,最好不要通过共享对象来管道函数,并且需要处理时序问题。更好的方法是在对象上存储属性并设置范围。$在你的dir2中观察它们。