5

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

stilllife
  • 1,776
  • 1
  • 18
  • 38
  • The ng-repeat has its own scope probably, many other directives can create their own scopes so it's best not to rely on `$parent`. Scope properties are inherited, so if you want to make sure you have access to a certain element in a child you can always create an object, like `$scope.secondData = {};` and access `secondData` on your scope. – Jason Goemaat Dec 26 '14 at 22:34
  • But in my case I would like to `$broadcast` something form the grandparent after a certain function on the grandchild. So probably `$rootScope` is the best option? – stilllife Dec 26 '14 at 22:40

3 Answers3

4

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;
});
Community
  • 1
  • 1
Asaf David
  • 416
  • 3
  • 7
0

to answer your comment's question, yes, you should use the $rootScope as a message broker in your system.

Regarding the specifics, this is related to the directive's scope, a better way to handle this code (for others that may stumble on this question) is the following:

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

this alleviates issues with scoping and clearly allows access to the proper controller within a directive.

Born2Code
  • 1,055
  • 6
  • 13
0

Per your comment on wanting to broadcast, you could use a function like this and call it from the child:

// in MainController:
$scope.broadcastMain = function(message) {
    $scope.$broadcast(message);
};

Then that function is inherited, but it's closure will have $scope be the same scope it is created on, so you can then use $scope.broadcast() to get to its children. (codepen)

Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113