rpabon
rpabon

Reputation: 1201

Communication between sibling directives inside isolated scope

I am trying to let one child directive to tell its sibling some information has been updated.

I have this markup:

  <rp-nav>
     <rp-nav-item cat="1"></rp-nav-item>
     <rp-nav-item cat="2"></rp-nav-item>
     <rp-nav-item cat="3"></rp-nav-item>

     <rp-flyout></rp-flyout>
  </rp-nav>

  <hr>

  <rp-nav>
    <rp-nav-item cat="one"></rp-nav-item>
    <rp-nav-item cat="two"></rp-nav-item>
    <rp-nav-item cat="three"></rp-nav-item>

    <rp-flyout></rp-flyout>
  </rp-nav>

I am using events in this way:

angular.module('rpNavItem', [])
  .directive('rpNavItem', function() {
    return {
        restrict: 'E',
        controller: function($scope) {},
        controllerAs: 'ctrl',
        bindToController: true,
        // scope: true,
        template: function(el, attrs) {
          return  '<div>Item ' + attrs.cat + '</div>';
        },
        link: function(scope, el, attrs, ctrl) {
            el.on('click', function(){
                scope.$broadcast('selectedCat', attrs.cat);
            });
        }
    };
});

angular.module('rpFlyout', []).directive('rpFlyout', function() {
  return {
    restrict: 'E',
    controllerAs: 'ctrl',
    bindToController: true,
    // scope:true,
    controller: function($scope) {},
    template: '<p style="background-color: lightblue">{{category}}</p>',
    link: function(scope) {
      scope.$on('selectedCat', function(event, value){
        scope.category = value;
        scope.$digest();
        console.log(value);
      });
    }
  };
});

The problem is that every time I click on one item, both flyouts get update and I just want the corresponding one updated. How can I achieve this? Is this the correct way to pass information between two sibling directives confined inside a parent isolated scope?

Plunker

Upvotes: 0

Views: 189

Answers (1)

aghidini
aghidini

Reputation: 3010

Every isolate scope is child of the same $rootScope so you cannot use the events to separate directives. $broadcast-ed events are used for global (up to the root) communication.

To communicate directly with directives you can use the require property of directives, so rpNavItem can require rpNav and use rpNav to share state.

rpFlyout is not needed for this example, javascript:

angular.module('app', ['rpNav']);

angular.module('rpNav', [])
    .directive('rpNav', function() {
        return {
            scope: true,
            controller: ['$scope', function ($scope) {
              this.setSelectedCat = function (cat) {
                $scope.category = cat;
              }
            }]
        };
    });

angular.module('rpNav')
    .directive('rpNavItem', function() {
        return {
            restrict: 'E',
            scope: {
              cat: '@'
            },
            template: function (el, attrs) {
              return  '<div>Item ' + attrs.cat + '</div>';
            },
            require: '^rpNav',
            link: function (scope, el, attrs, rpNav) {
                el.on('click', function(){
                    scope.$apply(function () {
                      rpNav.setSelectedCat(scope.cat);
                    });
                });
            }
        };
    });

The HTML can become something like:

<div rp-nav>
  <rp-nav-item cat="1"></rp-nav-item>
  <rp-nav-item cat="2"></rp-nav-item>
  <rp-nav-item cat="3"></rp-nav-item>

  <p style="background-color: lightblue">{{category}}</p>
</div>

<hr>

<div rp-nav>
  <rp-nav-item cat="one"></rp-nav-item>
  <rp-nav-item cat="two"></rp-nav-item>
  <rp-nav-item cat="three"></rp-nav-item>

  <p style="background-color: lightblue">{{category}}</p>
</div>

If you wish to keep rpFlyout directive for some reason you can make it have isolated storage too and pass the current category in an attribute (untested code):

<rp-flyout category="category"></rp-flyout>

Directive:

[...]
.directive('rpFlyout', function() {
        return {
            scope: {
              'category': '='
            },
            template: '<p style="background-color: lightblue">{{category}}</p>'
        };
    });

Upvotes: 1

Related Questions