Why can't the $rootScope be accessed in the template of a directive with isolate scope?

JavascriptAngularjsAngularjs Directive

Javascript Problem Overview


With isolate scope the template of the directive does not seem to be able to access the controller ('Ctrl') $rootScope variable which, however, does appear in the controller of the directive. I understand why the controller ('Ctrl') $scope variable isn't visible in the isolate scope.

HTML:

<div ng-app="app">
    <div ng-controller="Ctrl">
        <my-template></my-template>
    </div>
    
    <script type="text/ng-template" id="my-template.html">
        <label ng-click="test(blah)">Click</label>
    </script>
</div>

JavaScript:

angular.module('app', [])
    .controller('Ctrl', function Ctrl1($scope,  $rootScope) {
        $rootScope.blah = 'Hello';
        $scope.yah = 'World'
    })
    .directive('myTemplate', function() {
        return {
            restrict: 'E',
            templateUrl: 'my-template.html',
            scope: {},
            controller: ["$scope", "$rootScope", function($scope, $rootScope) {
                console.log($rootScope.blah);
                console.log($scope.yah);,
                
                $scope.test = function(arg) {
                    console.log(arg);
                }
            }]
        };
    });

JSFiddle

The variable is accessed with no isolate scope - as can be seen by commenting the isolate scope line:

        // scope: {},

Javascript Solutions


Solution 1 - Javascript

You can try this way out using $root.blah

Working Code

html

 <label ng-click="test($root.blah)">Click</label>

javascript

  angular.module('app', [])
    .controller('Ctrl', function Ctrl1($scope,  $rootScope) {
        $rootScope.blah = 'Hello';
        $scope.yah = 'World'
    })
    .directive('myTemplate', function() {
        return {
            restrict: 'E',
            templateUrl: 'my-template.html',
            scope: {},
            controller: ["$scope", "$rootScope", function($scope, $rootScope) {
                console.log($rootScope.blah);
                console.log($scope.yah);
                
                $scope.test = function(arg) {
                    console.log(arg);
                }
            }]
        };
    });

Solution 2 - Javascript

Generally, you should avoid using $rootScope to store values you need to share between controllers and directives. It's like using globals in JS. Use a service instead:

A constant (or value ... use is similar):

.constant('blah', 'blah')

https://docs.angularjs.org/api/ng/type/angular.Module

A factory (or service or provider):

.factory('BlahFactory', function() {
    var blah = {
        value: 'blah'
    };
    
    blah.setValue = function(val) {
      this.value = val;
    };
    
    blah.getValue = function() {
        return this.value;
    };
    
    return blah;
})

Here is a fork of your Fiddle demonstrating how you might use either

Solution 3 - Javascript

  1. Because of the isolate scope $scope in your controller Ctrl and in the directive controller don't refer to the same scope - let's says we have scope1 in Ctrl and scope2 in directive.

  2. Because of the isolate scope scope2 do not prototypicallly inherit from $rootScope ; so if you define $rootScope.blah there is no chance you can see it in scope2.

  3. What you can access in your directive template is scope2

If I sum it up, here is the inheritance schema

    _______|______
    |            |
    V            V
$rootScope     scope2
    |
    V
  scope1


$rootScope.blah
> "Hello"
scope1.blah
> "Hello"
scope2.blah
> undefined

Solution 4 - Javascript

I know this an old question. But it didn't satisfy my inquisition about why the isolated scope won't be able to access properties in the $rootscope.

So I dug in the angular lib and found -

$new: function(isolate) {
  var ChildScope,
      child;

  if (isolate) {
    child = new Scope();
    child.$root = this.$root;
    child.$$asyncQueue = this.$$asyncQueue;
    child.$$postDigestQueue = this.$$postDigestQueue;
  } else {

    if (!this.$$childScopeClass) {
      this.$$childScopeClass = function() {
        // blah blah...
      };
      this.$$childScopeClass.prototype = this;
    }
    child = new this.$$childScopeClass();
  }

This is the function called by angular whenever a new scope is created. Here it's clear that any isolated scope is not prototypically inheriting the rootscope. rather only the rootscope is added as a property '$root' in the new scope. So we can only access the properties of rootscope from the $root property in the new isolated scope.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questioncamden_kidView Question on Stackoverflow
Solution 1 - JavascriptNidhish KrishnanView Answer on Stackoverflow
Solution 2 - JavascriptMarc KlineView Answer on Stackoverflow
Solution 3 - JavascriptThomas GuilloryView Answer on Stackoverflow
Solution 4 - JavascriptAdarsh SharmaView Answer on Stackoverflow