Search code examples
angularjstransclusion

AngularJS : ng-transclude in directive and sending events to parent


I have custom directives like this:

<fold fold-name="Musique et sons">
    <sound-button height=60  song-name="Why Make..."></sound-button>
</fold>

fold directive has such template:

<button class="fold" ng-click="showMe = !showMe">{{foldName}} ({{nb}})</button>
<div class="fold" ng-transclude ng-show="showMe"></div>

In the sound-button controller, I have to do this:

$scope.$parent.$$prevSibling.$emit('foldstop')

If I want the controller to receive the event:

      $scope.$on 'foldplay', (sender, evt) ->

What's happening: creates 3 scopes :

 <fold> Scope
 <ng-transclude scope> which has no model, thus no controller.
      <sound-button scope> 

In sound-button directive, $scope.$emit would hit the ng-transclude scope which is a sibling of the scope I want to hit.

So I'm using $scope.$parent.$prevSibling to hit the right controller. It works, but is there a better way ?

Thanks !


Solution

  • You could try require if your directives are related.

    app.directive('soundButton', function() {
        return {
          restrict: 'E',
          scope: {
            songName: '@'
          },
          require:"^fold", 
          link : function (scope,element,attrs,foldController){//inject fold controller 
               foldController.callFold("message"); //use the controller to communicate
          }
        };
    });
    
    app.directive('fold', function() {
        return {
          restrict: 'E',
          scope: {
            foldName: '@'
          },
          templateUrl: 'fold.html',
          transclude:true,
    
          controller:function(){ //declare the controller to be used by child directives.
              this.callFold = function (message){
                alert(message);
              }
          }
        };
    });
    

    DEMO

    You cannot use $scope.$emit because your directives' scopes don't have parent/child relationship. You could refer to this discussion for more information.

    Update:

    If your directives are not related and you need to have parent/child relationship, you could try custom transclusion:

    app.directive('fold', function() {
        return {
          restrict: 'E',
          scope: {
            foldName: '@'
          },
          templateUrl: 'fold.html',
          transclude:true,
    
          compile: function (element, attr, linker) {
              return {
               pre: function (scope, element, attr) {
                 linker(scope, function(clone){ //bind the scope your self
                     element.children().eq(1).append(clone); // add to DOM
                 });
               },
               post: function postLink(scope, iElement, iAttrs) {  
                   scope.$on("foldplay",function(event,data){
                     alert(data);
                   });
                }
              };
          }
        };
    });
    

    DEMO (click on the button and then click on the shown text)