Angularjs - $rootScope in directive link function
Asked Answered
H

6

49

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!

Hemistich answered 12/2, 2014 at 18:44 Comment(0)
S
54

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

Slipshod answered 12/2, 2014 at 19:2 Comment(4)
ah! I understand. As Kurt pointed out it is not the best way to set properties of the app in angular but I needed to understand the rootScope and this explains it better. Thank you. Do you happen t know how $root scope is to be accessed in the link function of the directive?Hemistich
You need to think of $rootScope as being the global namespace and you want to avoid cluttering it. You can inject it in your directive but the whole point of directives is to be a component without too many outside dependencies. If you need something from $rootScope you might consider refactoring it into a service.Slipshod
It makes sense. I suppose I was resorting to using any kin dof equivalent to globals on account of having to make data x available at all times and not knowing how else to approach it. I DO use localStorage for some stuff but now looking at cacheFactory and the extended version of it,, and how they use localstorage, I am seeing the light. Thanks!Hemistich
Setting scope: true will make it work. By setting it to an empty hash {} or false isolates the scope and breaks the inheritance chain. (See Directive Definition Object, scope) code.angularjs.org/1.4.1/docs/api/ng/service/$compile (my example is against 1.2.1 but it hasn't changed from what I can tell).Slipshod
S
70

You can do this way:

{{$root.rsLabels.welcome}}
Sable answered 28/5, 2014 at 8:32 Comment(4)
Thanks, didn't that. I use link: function(scope, ...) { scope.$root } to access $rootScope from a directive.Indelicacy
Yeah, I mean I was googling for accessing the $rootScope from directive and I didn't know about the $root property. Thanks for the answer.Indelicacy
Should be the correct answer... Thanks, anyway for providing your solution.Roger
Solid solution. A+Overhang
S
54

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

Slipshod answered 12/2, 2014 at 19:2 Comment(4)
ah! I understand. As Kurt pointed out it is not the best way to set properties of the app in angular but I needed to understand the rootScope and this explains it better. Thank you. Do you happen t know how $root scope is to be accessed in the link function of the directive?Hemistich
You need to think of $rootScope as being the global namespace and you want to avoid cluttering it. You can inject it in your directive but the whole point of directives is to be a component without too many outside dependencies. If you need something from $rootScope you might consider refactoring it into a service.Slipshod
It makes sense. I suppose I was resorting to using any kin dof equivalent to globals on account of having to make data x available at all times and not knowing how else to approach it. I DO use localStorage for some stuff but now looking at cacheFactory and the extended version of it,, and how they use localstorage, I am seeing the light. Thanks!Hemistich
Setting scope: true will make it work. By setting it to an empty hash {} or false isolates the scope and breaks the inheritance chain. (See Directive Definition Object, scope) code.angularjs.org/1.4.1/docs/api/ng/service/$compile (my example is against 1.2.1 but it hasn't changed from what I can tell).Slipshod
C
7

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)

Crankshaft answered 12/2, 2014 at 18:48 Comment(2)
Yes, I figured using $rootScope like this is not the best of ideas. Thanks for the suggestion btw. I am looking for better ways to create widely accessible objects that I can use site wide from various directives though. I find that documentation on angular site is completely useless (like much of that site) because it neither explains anything not demonstrates real use case or at least logical use cases. In my case all directives across the site must have access to user information,,, and a few other bits of information, at all times. Best way? Do you know of useful. good documentation maybe?Hemistich
Hey, I found some good info around plus this jmdobry.github.io/angular-cache/guide.html, and a useful YT video .. Thanks for this tip! I appreciate it a lot :) and I think I will look deeper into it as an actual use approach in what I am working on. Mike however did address my question with an explanation so I will mark his answer as the correct answer.Hemistich
S
7

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;
    });
  };
}]);
Syncopation answered 5/9, 2016 at 16:14 Comment(0)
L
4

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);
            })
        }
    };  
}]);
Lightening answered 7/8, 2015 at 1:28 Comment(0)
P
1

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.

Pyroxylin answered 21/3, 2016 at 9:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.