Search code examples
angularjsangularjs-scopeparent

Angular $parent and $parent.$parent are the same controller when using ng-repeat


I would like to understand why when using a ng-repeat with a certain controller on the item of the repeat, the parent of that item and the grandfather of that item are the same controller. I was expecting the grandfather to be the parent of the parent controller, not the same controller.

Here the code to clarify this

HTML

<div ng-controller="MainController">
  {{name}}
  <div ng-controller="SecondController">
  {{name}}
    <ul>
      <li ng-repeat="item in list" ng-controller="ItemController">
         {{item.name}} {{$parent.name}} {{myDad}} {{myGrandpa}}
      </li>
    </ul>
    <div ng-controller="ThirdController">
         {{name}} {{$parent.name}} {{myDad}} {{myGrandpa}}
    </div>
  </div>
</div>

JS

angular.module('app', []);


angular.module('app')
  .controller('MainController', function($scope) {
  $scope.name = "MainController";
 })
.controller('SecondController', function($scope) {
  $scope.name = "SecondController";
  $scope.list = [
   {'name': 'aaa'}
  ];
})
.controller('ItemController', function($scope) {
  $scope.name = "ItemController";
  $scope.myDad = $scope.$parent.name;
  $scope.myGrandpa = $scope.$parent.$parent.name;
})
.controller('ThirdController', function($scope) {
  $scope.name = "ThirdController";
  $scope.myDad = $scope.$parent.name;
  $scope.myGrandpa = $scope.$parent.$parent.name;
});

Here a CodePen


Solution

  • In Angular, $scope inherits and not controllers.

    Each controller created by ng-controller creates a new $scope (childScope) that inherits from the $scope it exists in. I think you might want to read this answer to understand how exactly $scope inherits (prototypical inheritance).

    Please not that you don't have to use "$parent" in order to get the name of the parent $scope, for example if you would remove $scope.name from ItemController and would try to bind {{name}} your template will be compiled and linked and the value of {{name}} would be "SecondController" in your example.

    Regarding the $broadcast, You should try to avoid using $rootScope.$broadcast as it will dispatch an event down to all of the $scopes in your application. I'm not sure what your use case is, but if you want to execute a method that is defined in your "MainController" for instance, you can just call it for example

    angular.module('app', []);
    
    
    angular.module('app')
      .controller('MainController', function($scope) {
      $scope.name = "MainController";
      $scope.doSomething = function() {
        console.log("Do it");
      }
     })
    .controller('SecondController', function($scope) {
      $scope.name = "SecondController";
      $scope.list = [
       {'name': 'aaa'}
      ];
    })
    .controller('ItemController', function($scope) {
      $scope.name = "ItemController";
      $scope.myDad = $scope.$parent.name;
      $scope.myGrandpa = $scope.$parent.$parent.name;
      $scope.clickItem = function() {
         console.log("Item clicked");
         $scope.doSomething(); // The method from "MainController" will be executed unless ItemController is isolated 
      }
    })
    .controller('ThirdController', function($scope) {
      $scope.name = "ThirdController";
      $scope.myDad = $scope.$parent.name;
      $scope.myGrandpa = $scope.$parent.$parent.name;
    });