Take the 2-minute tour ×

To set the stage - this is not happening within a single scope, where I can bind a simple attribute. The element I want to fade in/out does not sit inside a controller, it sits inside the ng-app (rootScope). Further, the button that's clicked is in a child scope about 3 children deep from root.

Here is how I'm currently solving this:

HTML (sitting in root scope):

<ul class="nav-secondary actions" 
  darthFader fadeDuration="200" 

Where darthFader is my directive.



function() {
        return {
            restrict: 'A',
            link: function($scope, element, attrs) {
                $scope.$on(attrs.fadeevent, function(event,options) {
                    $(element)["fade" + options.fade || "In"](attrs.fadeduration || 200);


So here I'm creating an event handler, specific to a given element, that is calling fadeIn or fadeOut, depending on an option being passed through the event bus (or defaulting to fadeIn/200ms).

I am then broadcasting an event from $rootScope to trigger this event:

$rootScope.$broadcast('darthFader:secondaryNav', { fade: "Out"});

While this works, I'm not crazy about creating an event listener for every instance of this directive (while I don't anticipate having too many darthFader's on a screen, it's more for the pattern I would establish). I'm also not crazy about coupling my attribute in my view with an event handler in both my controller & directive, but I don't currently have a controller wrapping the secondary-nav, so I'd have to bind the secondaryNav to $rootScope, which I don't love either. So my questions:

  1. Is there a way to do this without creating an event handler every time I instantiate my directive? (maybe a service to store a stateful list of elements?)
  2. How should I decouple my view, controller & directive?
  3. Any other obvious questions I'm missing?


share|improve this question
have you finish it? would you like to share it with me? thank you –  PyWebDesign Oct 21 at 18:14
see the accepted answer –  Scott Silvi Oct 21 at 19:14

2 Answers 2

up vote 3 down vote accepted

You mention in your question


I believe if I were to write this same functionality, I would put the element in its own controller--controllers are responsible for managing the intersection of the view and the model, which is exactly what you're trying to do.

myApp.controller('NavController', function($scope) {
  $scope.fadedIn = false;
<ul ng-controller="NavController"
  class="nav-secondary actions" 
  darthFader fadeDuration="200" 
myApp.directive('darthFader', function() {
  return {
    restrict: 'A',
    link: function($scope, element, attrs) {
      var duration = attrs.fadeDuration || 200;
      $scope.$watch(attrs.fadeShown, function(value) {
        if (value)

If you're worried about sharing the fade in/out state between multiple controllers, you should create a service to share this state. (You could also use $rootScope and event handlers, but I generally find shared services easier to debug and test.)

myApp.value('NavigationState', {
  shown: false

myApp.controller('NavController', function($scope, NavigationState) {
  $scope.nav = NavigationState;

myApp.controller('OtherController', function($scope, NavigationState) {
  $scope.showNav = function() {
    NavigationState.shown = true;

  $scope.hideNav = function() {
    NavigationState.shown = false;
<ul ng-controller="NavController"
  class="nav-secondary actions" 
  darthFader fadeDuration="200" 

<!-- ..... -->

<div ng-controller="OtherController">
  <button ng-click="showNav()">Show Nav</button>
  <button ng-click="hideNav()">Hide Nav</button>
share|improve this answer
Thanks for the detailed sample code. I don't think this resolves the issue of .. how do I update $scope.fadedIn in the parent scope from a nested child scope? Do I $emit up to it? I don't want shared scope in my directive (I want these events to be independent of one another - read: fadeIn/fadeOut elements independent of one another)... if I $emit, how do I make sure I'm only capturing the event to hide/show this particular element? –  Scott Silvi May 14 '13 at 18:25
You would update the "parent" scope by getting a reference to NavigationState, either by accessing nav directly on the scope, or passing it into a directive via an attribute (e.g. with an isolate scope) or shared service (depending on how generic they are). –  Brandon Tilley May 14 '13 at 19:07
So I wasn't planning to go the service route because I don't need a singleton, I need an instance of the fadedIn variable per view. I suppose I could still create a service that exposes a property per view (e.g. secondaryNavFadedIn, someOtherElementFadedIn, etc) then bind my parent $scope.fadedIn = MyService.secondaryNavFadedIn. From my child controller, inject the service & set the property on the service, which will then be picked up by the $watcher in my directive. That what you're suggesting? –  Scott Silvi May 14 '13 at 19:46
Something along those lines; it's hard to say exactly without more information about your app. Your service can also be a function that looks up an object based on some key name, or you can even have multiple services. –  Brandon Tilley May 14 '13 at 22:19

Create a custom service, inject it in the controller. Call a method on that service that will do the fade-in/fade-out etc. Pass a parameter to convey additional information.

share|improve this answer
Misko said in his best practices talk that you shouldn't do DOM manipulation in a service. That's why I haven't done what I proposed in question 1. Thoughts? –  Scott Silvi May 14 '13 at 3:47
I understand. But you don't have a whole lot of options. Also we are not adding/removing dom elements. We are just fading the element. Nothing major. –  Ketan May 14 '13 at 3:55

Your Answer


