0

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?

r3plica
  • 13,017
  • 23
  • 128
  • 290

4 Answers4

1

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?

Community
  • 1
  • 1
Matt Way
  • 32,319
  • 10
  • 79
  • 85
0

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
       }
   }
};
sma
  • 9,449
  • 8
  • 51
  • 80
0

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!

Jhey
  • 1,377
  • 7
  • 10
0

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)
A G
  • 21,087
  • 11
  • 87
  • 112
  • This is already what I do, but as my "client" requires an array of "servers" I end up with a fairly complicated "client" with my functions called from many "servers". I would like each "server" to handle it's own functions but only after the "client" has finished loading. – r3plica Sep 03 '15 at 17:18
  • perhaps you would like to update the original post with directive usage example in html. – A G Sep 03 '15 at 17:35
  • what I don't understand is, its well known that inner directive link is called before outer one. So if your waiting for DOM complete you can use $timeout, which executes after DOM has completed rendering. – A G Sep 03 '15 at 17:42
  • so if I use a timeout, I can call a function after any SVGs have loaded? – r3plica Sep 03 '15 at 20:35
  • You can use the DOM load event. I have added this to above answer. – A G Sep 03 '15 at 21:01
  • I am fairly certain I have tried the load before but because I am setting the SVG path inside the directive and it applies it to an ng-include, it will try to run before the SVG loads which is no good. – r3plica Sep 04 '15 at 09:32