44

With the original way to define controllers, accessing the parent's scope was fairly trivial, since the child scope prototypically inherits from its parent.

app.controller("parentCtrl", function($scope){
   $scope.name = "Parent";
})
.controller("childCtrl", function($scope){
   $scope.childName = "child of " + $scope.name;
});

<div ng-controller="parentCtrl">
   {{name}}
   <div ng-controller="childCtrl">
      {{childName}}
   </div>
</div>

The Controller-As approach seems to be the recommended way to declare a controller. But with Controller-As, the above approach no longer works.

Sure, I can access the parent scope with pc.name from the View:

<div ng-controller="parentCtrl as pc">
   {{pc.name}}
   <div ng-controller="childCtrl as cc">
      {{cc.childName}}
   </div>
</div>

I do have some issues with this (potential for spaghetti code), but this question is about accessing the parent scope from the child controller.

The only way I can see this working is:

app.controller("parentCtrl", function(){
   this.name = "parent";
})
.controller("childCtrl", function($scope){
   $scope.pc.name = "child of " + $scope.name;
   // or
   $scope.$parent.pc.name = "child of " + $scope.name;

   // there's no $scope.name
   // and no $scope.$parent.name
});

So now, the child controller needs to know about "pc" - except, this should (in my mind) be restricted to the view. I don't think a child controller should know about the fact that a view decided to declare a ng-controller="parentCtrl as pc".

Q: What's the right approach then?

EDIT:

Clarification: I'm not looking to inherit a parent controller. I am looking to inherit/change the shared scope. So, if I was to amend the first example, I should be able to do the following:

app.controller("parentCtrl", function($scope){
   $scope.someObj = {prop: "not set"};
})
.controller("childCtrl", function($scope){
   $scope.someObj.prop = "changed";
});
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • 1
    Angular's "controllers" are really more of a "view helper" or a "view model". The style guides that suggest using `controllerAs` actively encourage you to use `pc` directly or gloss over this situation. I suppose you could set up an inheritance chain between `parentCtrl` and `childCtrl`, e.g. `childCtrl.prototype = Object.create(parentCtrl.prototype)` and then in your `childCtrl` constructor, `this.name` would refer to the parent's name before you shadow it. – Sacho Oct 30 '14 at 07:50

3 Answers3

62

After researching, I came to the following realization:

Controller-As approach is NOT a substitute for using $scope. Both have their place, and can/should be used together judiciously.

  1. $scope does exactly what the name implies: i.e. it defines ViewModel properties on the $scope. This works best for sharing scope with nested controllers that can use the $scope to drive their own logic or to change it.
  2. Controler-As defines the entire controller object as a ViewModel with a named scope (via the controller's alias). This works best only in the View (but not other controllers), if the View decides if it wants to reference a specific controller ViewModel.

Here's an example:

var app = angular.module('myApp', []);

// Then the controllers could choose whether they want to modify the inherited scope or not:
app.controller("ParentCtrl", function($scope) {
    this.prop1 = {
      v: "prop1 from ParentCtrl"
    };
    $scope.prop1 = {
      v: "defined on the scope by ParentCtrl"
    };
  })
  .controller("Child1Ctrl", function($scope) {})
  .controller("Child2Ctrl", function($scope) {
    // here, I don't know about the "pc" alias
    this.myProp = $scope.prop1.v + ", and changed by Child2Ctrl";
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>

<body ng-app="myApp">
  <div ng-controller="ParentCtrl as pc">
     <div ng-controller="Child1Ctrl">
        <div>I know about the "pc" alias: {{pc.prop1.v}}</div>
     </div>
     <div ng-controller="Child2Ctrl as ch2">
       <div>I only care about my own ViewModel: {{ch2.myProp}}</div>
    </div>
  </div>
Manoj Shevate
  • 742
  • 4
  • 18
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • 2
    So are you basically saying that it's not feasible to access a parent's scope inside a child when using "controller as"? I don't consider the approach listed by the OP ($scope.$parent.pc.name) to be feasible. – d512 Oct 13 '16 at 20:50
7

You should do like :

html

<div ng-controller="ChildController as child">
    <button type="button" ng-click="child.sayMe()">Say me!</button>
</div>

js

var app = angular.module('myApp', [])
app.controller('BaseController',function() {
    this.me = 'Base';
    this.sayMe= function() {
        alert(this.me);
    }
});
app.controller('ChildController', function($scope, $controller) {
    var controller = $controller('BaseController as base', {$scope: $scope});
    angular.extend(this, controller);
    this.me = 'Child';
});

take a look at https://docs.angularjs.org/guide/controller

Whisher
  • 31,320
  • 32
  • 120
  • 201
  • My intent (sorry, if it was not properly conveyed) was not to inherit a base controller, but rather to inherit the scope. I don't want to know which parent controller is there - I just want to rely on scope (as per my first example). – New Dev Oct 30 '14 at 18:53
  • ok so the only way I know is using $parent.$scope or $rootScope – Whisher Oct 30 '14 at 20:05
  • @Whisher: it was really helpful. One question on this. What is the need of putting 'BaseController as base' here? I can simply do 'BaseController' right since anyway, the purpose is to inherit the properties/methods. Please correct me if I am wrong – Jinto Feb 12 '15 at 07:59
  • @jitoppy if you don't use the 'controller as' syntax you can do without any problem 'BaseController' – Whisher Feb 16 '15 at 07:55
1

For anyone looking to simply access the parent scope programmatically, use the $scope service, find the parent scope, and access the name used for the parent scope's controllerAs, e.g.:

$scope.$parent.someName.doSomething();

Adam Reis
  • 4,165
  • 1
  • 44
  • 35