1

I have two examples in favor of the above statement -

1) When using $scope (http://plnkr.co/edit/kFM77mVReS7AUwZsNzCV?p=preview) -

<!DOCTYPE html>
<html>

  <head>
    <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
    <script>
      angular
          .module('myApp', [])
          .directive('directive1', function() {
            return {
              controller: ['$scope', function($scope) {
                $scope.name = 'Directive1';
              }]
            };
          })
          .directive('directive2', function() {
            return {
              controller: ['$scope', function($scope) {
                $scope.name = 'Directive2';
              }],
              scope: {}
            };
          })
          .directive('directive3', function() {
            return {
              template: 'I am {{name}}'
            };
          });
    </script>
  </head>

  <body ng-app='myApp'>
    <directive1>
      <directive2>
        <directive3>
        </directive3>
      </directive2>
    </directive1>
  </body>

</html>

2) When using controllerAs(http://plnkr.co/edit/zmIRa1t87ZIMDS6X5rNo?p=preview)-

 <!DOCTYPE html>
<html>

  <head>
    <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
    <script>
      angular
          .module('myApp', [])
          .directive('directive1', function() {
            return {
              controller: function() {this.name = 'Directive1';},
              controllerAs: 'ctrl1'
            };
          })
          .directive('directive2', function() {
            return {
              controller: function() {this.name = 'Directive2';},
              controllerAs: 'ctrl2',
              transclude: true,
              template: '<ng-transclude></ng-transclude>',
              scope: {}
            };
          })
          .directive('directive3', function() {
            return {
              template: 'I am {{ctrl1.name}}'
            };
          });
    </script>
  </head>

  <body ng-app='myApp'>
    <directive1>
      <directive2>
        <directive3>
        </directive3>
      </directive2>
    </directive1>
  </body>

</html>

The output of both the codes is - I am Directive1 which shows that directive3 inherits the scope of directive1(it won't access the scope of directive2 since it has an isolated scope) which proved me wrong in assuming that an isolated scope would break the inheritance chain between its parent directive and its child directives and thus none of its child directive would be able to access the scope of any of its ancestor directives.

Am I missing something here or is my concept of scope inheritance completely wrong?

2 Answers2

1

The output <...> proved me wrong in assuming that an isolated scope would break the inheritance chain between its parent directive and its child directives

The proof itself is wrong. This behaviour is specific to template-less directives and is similar to transclusion. In the code above directive1 has no own scope, and $scope.name = 'Directive1' is set on root scope. And both directive1 and directive2 are compiled with root scope because they have no templates and no own scopes.

The said assumption will be correct if directives have their own templates, e.g.:

  .directive('directive2', function() {
    return {
      template: '<directive3>'
      controller: ['$scope', function($scope) {
        $scope.name = 'Directive2';
      }],
      scope: {}
    };
  })
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • thanks for the explanation. If that is the case then can you please explain the same output in [link](http://plnkr.co/edit/Gp8SZ0TcPijRcxZdQcII?p=preview). I just changed the `scope` property of _directive1_ as true, so that _directive1_ creates a new scope. Seems like this has more to do than root scope itself. – Rachit Belwariar Oct 27 '17 at 17:20
  • 1
    @RachitBelwariar Yes, in this case `directive3` scope is inherited from non-isolated parent directive scope. Actually, the behaviour of template-less directives is undocumented, to my knowledge. And it becomes even more complex if template-less directives involve scope bindings. But since template-less directives are rarely needed and should involve scope even more rarely, this is not a big deal. With the exception of slim ngClick-like directives you will always have a template, and the behaviour of scope in templated directives is very straightforward. – Estus Flask Oct 27 '17 at 17:53
  • Yes, I also tried looking up various articles - "template less directive" and I must admit that none of them was relevant. Anyways, I am going to mark this as the accepted answer. Can you fix a typo in your answer - "...And both **directive1** and **directive1** are compiled with root scope..." (**directive1** occurring twice)? Thanks again. – Rachit Belwariar Oct 27 '17 at 18:49
  • Sure. I believe I've had other answers that were dedicated to this topic, but I can't find them. You can check also this one, it's related and I guess it sheds some light on how compiler works in such cases https://stackoverflow.com/a/42735839/3731501 – Estus Flask Oct 27 '17 at 19:09
0

Use scope:true in all your 3 directives and it will able to access all scopes of the parents

Note: scope:true will inherit the properties from the parent, but scope:{} will not inherit the properties from the parent.

<!DOCTYPE html>
<html>

<head>
  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
  <script>
    angular
      .module('myApp', [])
      .directive('directive1', function() {
        return {
          controller: ['$scope', function($scope) {
            $scope.name = 'Directive1';
          }],
          scope: true
        };
      })
      .directive('directive2', function() {
        return {
          controller: ['$scope', function($scope) {
            $scope.name = 'Directive2';
          }],
          scope: true
        };
      })
      .directive('directive3', function() {
        return {
          template: 'I am {{name}}',
          scope: true
        };
      });
  </script>
</head>

<body ng-app='myApp'>
  <directive1>
    <directive2>
      <directive3></directive3>
    </directive2>
  </directive1>
  
  <br>
  
  <directive1>
    <directive3></directive3>
  </directive1>

  <br>

  <directive2>
    <directive3></directive3>
  </directive2>

</body>

</html>
ferhado
  • 2,363
  • 2
  • 12
  • 35
  • Thanks for your input. I am aware of your solution but I am more interested in knowing why this problem occurs rather than how to solve this. – Rachit Belwariar Oct 27 '17 at 16:35
  • Both scope: true and scope:{} will create a child scope for the directive, but scope:true will prototypically inherit the properties from the parent, where as scope:{} will not inherit the properties from the parent – ferhado Oct 27 '17 at 16:41
  • For more details see this https://github.com/angular/angular.js/wiki/Understanding-Scopes – ferhado Oct 27 '17 at 16:45