Angularjs - $rootScope in directive link function

Problem

I am asking this question because I am not quite clear on how to think of rootscope as a dependency passed to directives

I have a directive that needs to display some information from $rootScope ...

I thought I needed to pass the $rootScope to a directive but when I write a directive like this it seems to work.

.directive("myBar", function () {
 return {
    restrict: "E",
    transclude: true,
    replace: true,
    template:   '<div>' + 
                '<span ng-transclude></span>' + 
                '{{rsLabels.welcome}} {{rsUser.firstName}}!' + 
                '</div>'
}
})

When do I need to do this?

.directive("myBar", function ($rootScope) {
 return {
    restrict: "E",
    transclude: true,
    replace: true,
    template:   '<div>' + 
                '<span ng-transclude></span>' + 
                '{{rsLabels.welcome}} {{rsUser.firstName}}!' + 
                '</div>'
}
})

Can I and HOW do I use rootScope if I need it in the link function of the directive - or should I do it in the controller of the directive?

.directive("myBar", function ($rootScope) {
 return {
    restrict: "E",
    transclude: true,
    replace: true,
    link: function (scope, element, attrs, rootScope) {
        rootScope.rsUser = { firstName: 'Joe' };
        rootScope.rsUser = { welcome: 'Welcome' };
    },
    template:   '<div>' + 
                '<span ng-transclude></span>' + 
                '{{rsLabels.welcome}} {{rsUser.firstName}}!' + 
                '</div>'
}
})

My rootScope data is defined in run function

 .run(function ($rootScope) {

  $rootScope.rsLabels = { 
      welcome: 'Welcome'
  };

  $rootScope.rsUser = { 
     firstName: 'Joe'
  };
});

Thank you!

Problem courtesy of: GRowing

Solution

From my experiments \ experience, it seems that since all $scopes ultimately inherit from the $rootScope you will be able to access data on it without requesting it as a service, following standard javascript prototypical inheritance rules. If you were to set the scope property in your directive to false or {} you will find that you can no longer access it.

.directive("myBar", function($rootScope) {
    return {
        restrict: "E",
        scope: { /* Isolate scope, no $rootScope access anymore */ },
        transclude: true,
        replace: true,
        template: '<div>' + 
                  '<span ng-transclude></span>' + 
                  '{{rsLabels.welcome}} {{rsUser.firstName}}!' + 
                  '</div>'
    };
});

Example: http://jsbin.com/bequy/1/edit

Solution courtesy of: Mike Cheel

Discussion

After laboring away on this same question for quite some time, I thought it was worth noting something that was neglected in the first post. Here is my original code:

app.directive('countrymap', function() 
{
    return {
        link: function(scope, element, attrs) {
            scope.$watch("countryMap", function (newCountry, oldCountry) 
            {
                setTimeout( function() 
                {
                    //function body here    
                }, 100);
            })
        }
    };  
}]);

Aside from the more philosophical design question of whether or not you should even use $rootScope at all, there is one blatantly wrong thing with my code above that I feel was left out from Mike's solution - the reference to $rootScope. If you're like me and have segregated your directive and controller files you will need to modify your code as follows:

app.directive('countrymap', ['$rootScope', function($rootScope) 
{
    return {
        link: function(scope, element, attrs) {
            $rootScope.$watch("countryMap", function (newCountry, oldCountry) 
            {
                setTimeout( function() 
                { 
                    //function body here
                }, 100);
            })
        }
    };  
}]);

Yet, there is still one more nagging question: can I accomplish the same goal without referencing $rootScope in the directive? Indeed you can. You need to broadcast the change to the $rootScope property effectively letting all child scopes know about the change and watching for this change in the directive.

Controller:

$rootScope.countryMap = 'thiscountry_map';
$rootScope.$broadcast( "countryMapChanged", $rootScope.countryMap );

Directive:

app.directive('countrymapalt', [function() 
{
    return {
        link: function(scope, element, attrs) {
            scope.$on("countryMapChanged", function(event, map) 
            {
                setTimeout( function() 
                { 
                    //function body here
                }, 100);
            })
        }
    };  
}]);
Discussion courtesy of: user1991179

You can do this way:

{{$root.rsLabels.welcome}}
Discussion courtesy of: karaxuna

It's not recommended to use the root scope to set and get properties in your angular application. Try using the $cacheFactory, since that way you can also cache some values over various requests. ($cacheFactory docs)

Discussion courtesy of: Kurt Du Bois

Sometimes I have to use $scope.$root:

app.directive('setOrdinal', function() {
  return {
    link: function($scope, $element, $attr) {

      var steps = $scope.$root.steps;

      $scope.$watch(setOrdinal, function(value) {
        if (value)
        {
          // steps code here
        }
      });
    }
  };
});

app.controller('stepController', ['$scope', '$rootScope', 'GetSteps', function ($scope, $rootScope, GetSteps) {
  var s = $scope;
  var r = $rootScope;

  s.initialize = function(id)
  {
    GetSteps.get({id: id}, function(resp){
      r.steps = resp.steps;
    });
  };
}]);
Discussion courtesy of: oMiKeY

Another way is to create a service and throw that service access the $rootScope and other functions. I did it like this because of my environment...

app.service('myService', function ($rootScope) 
{
    this.removeItem = function (el) 
    {
       console.log('rootScope: ',$rootScope);
       return true;
    }
});


app.directive('draggable', function($document,myService) 
{
   return function(scope, element, attr) 
   {
        myService.removeItem({id:1})
   }
});

If you can, the best way is Mike solution. if not, try my solution.

Discussion courtesy of: Philip Enc

This recipe can be found in it's original form on Stack Over Flow.