r3plica
r3plica

Reputation: 13367

AngularJS directive broadcast issues

I have this directive (which I will cut down for brevity):

.directive('simpleConfigurator', function () {

    return {
        restrict: 'A',
        scope: {
            garment: '=simpleConfigurator',
            colours: '=',
            onComplete: '&'
        },
        require: ['designs', 'colours'],
        transclude: true,
        templateUrl: '/assets/tpl/directives/kit.html',
        link: function (scope, element, attrs, controllers) {

            scope.svgPath = 'assets/garments/' + scope.garment.slug + '.svg';

            // Executes after the svg has loaded
            scope.loaded = function () {

                scope.$broadcast('loaded', { loaded: true });
            };
        }
    };
})

The HTML for this looks like this:

<div ng-transclude></div>
<div id="{{ garment.slug }}" ng-include="svgPath" onload="loaded()"></div>

I am trying to get it to communicate with other directives. So in my designs directive, I have this:

.directive('designs', function () {
    return {
        restrict: 'A',
        controller: 'ConfiguratorDesignsDirectiveController',
        link: function (scope) {

            scope.$on('loaded', function () {

                console.log('we have loaded');
            });
        }
    }
})

I was hoping that I would get a console log stating we have loaded but I didn't. I assume it is because both "designs" and "colours" are considered the parents and the child is the directive that requires them.

Is there another way I can communicate with the parent directives or is there a way to get this to work?

Upvotes: 0

Views: 108

Answers (4)

A G
A G

Reputation: 22577

Your simpleConfigurator directive has its own isolate scope. You can see using console.log(scope) that scopes of simpleConfigurator and design directives are different. So when you broadcast from simpleConfigurator it never reaches design. Here is an article explaining this in detail.

http://www.bennadel.com/blog/2725-how-scope-broadcast-interacts-with-isolate-scopes-in-angularjs.htm

How to communicate between directives - I have found this article very helpful. http://blog.dudak.me/2014/angular-js-directive-communication-guide-part-1/
In your case you can perhaps use this strategy -

app.directive("server", function() {
  return {
    controller: function() {
      this.log = function(message) {
        console.log(message);
      };
    }
  };
});

app.directive("client", function() {
  return {
    require: "^server",
    link: function($scope, $elem, $attrs, serverCtrl) {
      serverCtrl.log("Hello, this is the client!");
    }
  };
});

You might want to use the DOM load event inside the link function - The load event is fired when a resource and its dependent resources have finished loading.

elem.addEventListener('load', callbackFunction, false)

Upvotes: 0

Jhey
Jhey

Reputation: 1377

Injecting the $rootScope and using $rootScope.$broadcast I believe is what you are trying to do although this isn't the ideal solution.

It may be better to use a form of pub/sub architecture between your parent controller and directives then make use of the $rootScope from your controller.

You can always pass event names to your directive that it can listen out for through attributes. Make use of $scope.$emit and $scope.$on to get this working how you desire.

This will promote the DRY principle and aim to make your directive more generic and reusable across your application which is one of the intentions with directives in general.

Hope that helps you out!

Upvotes: 0

Matt Way
Matt Way

Reputation: 33171

You have a couple of options:

You could inject $rootScope, and broadcast from there. Although this should work, it wouldn't be my first choice.

You can also use a controller on your parent directive, and pass that to the child directives, using it as a communication medium. It looks like you already have the controller setup. So instead of using $on inside the directive, why not forgo events all together. Just call a function on your controller.

This is also definitely relevant: What's the correct way to communicate between controllers in AngularJS?

Upvotes: 1

sma
sma

Reputation: 9597

Another possible problem here could be that simpleConfigurator is rendered before designs. So, the event is broadcast before any listeners are set up.

You could try requiring the parent directive in your child directive and then you'd have access to the parent controller:

In simpleConfigurator:

return {
   restrict : 'E',
   require : '^designs',
   link : function (scope, elem, attrs, designsController) {
        designsController.doStuff();
   }
}

In designs:

return {
   restrict : 'E',
   controller : function ($scope) {
       $scope.doStuff = function () {
           // do some stuff
       }
   }
};

Upvotes: 0

Related Questions