1

I was about to covert my code to use controller as syntax after reading a few articles about it. However, I was surprised that using the controller as syntax one can no longer use parent controller's variable directly, what I mean is, in the old way (with $scope), I can do this:

// <div ng-controller="ParentCtrl">
//   <div ng-controller="ChildCtrl"></div>
// </div>
// 
app.controller('ParentCtrl', function ($scope) {
  $scope.x.title = 'Some title';
});


app.controller('ChildCtrl', function ($scope) {
  console.log($scope.x.title);
});

But with controller as, I have to do this(thanks to this question):

// <div ng-controller="ParentCtrl as pc">
//   <div ng-controller="ChildCtrl as cc"></div>
// </div>
// 
app.controller('ParentCtrl', function () {
  this.x.title = 'Some title';
});


app.controller('ChildCtrl', function ($scope) {
  console.log($scope.pc.x.title);
});

This is quite annoying because 1). I have to know that in the html page my parent controller is named as pc. 2). I can not do a bulk search and replace of $scope => vm (or this) because it won't work if the property is inherited.

Can anyone please tell me what's the rationale behind this when controller as is introduced?

Am I right if I use a lot of scope inheritance then I should avoid controller as? Or is scope inheritance considered harmful in general and should be discouraged?

Community
  • 1
  • 1
swang
  • 5,157
  • 5
  • 33
  • 55

1 Answers1

3

No, you cannot do a mechanical search and replace because you currently have values from all your different scopes mixed together and controller-as syntax is designed to separate them. It is specifically to avoid the case where some parent scope uses title and then you've gone and used title again in a child scope and hidden the parent one. Or worse you thought you were updating the parent title when all you did was mask it in one child scope.

So you have to actually work out which parent scope contains each of the values that you want to access. That means you do indeed have to know the name used to reference that scope's model if your going to pull it out of the scope inheritance tree.

A better solution would be to use directives, or from angular 1.5 onward components. Instead of the child controller crawling up the scopes to get at parent values pass the desired values down into the directive/component as parameters. Then the parent becomes responsible for exposing the values it wants the child to access. Or you can use a directive or controller's require property to create a child that only works when embedded within a specific parent and the parent's controller will be bound directly into the child's controller.

Here's an example from the angular documentation. Note that inside the myPane controller you can access the parent controller as this.tabsCtrl and importantly it is the child that decides what name to use for the parent controller, not the parent:

angular.module('docsTabsExample', [])
.component('myTabs', {
  transclude: true,
  controller: function MyTabsController() {
    var panes = this.panes = [];
    this.select = function(pane) {
      angular.forEach(panes, function(pane) {
        pane.selected = false;
      });
      pane.selected = true;
    };
    this.addPane = function(pane) {
      if (panes.length === 0) {
        this.select(pane);
      }
      panes.push(pane);
    };
  },
  templateUrl: 'my-tabs.html'
})
.component('myPane', {
  transclude: true,
  require: {
    tabsCtrl: '^myTabs'
  },
  bindings: {
    title: '@'
  },
  controller: function() {
    this.$onInit = function() {
      this.tabsCtrl.addPane(this);
      console.log(this);
    };
  },
  templateUrl: 'my-pane.html'
});
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • I agree with your first paragraph, however, in the real code I follow the old idiom of dot syntax, which avoids the hidden parent problem. I have updated my question to reflect this. I'm sure yours is a fine solution, but migrating my application to use component is quite a big change and I thought controller as is a low hanging fruit...(I don't want to use directives either because I was once told detectives are better used for changing DOM) – swang Nov 22 '16 at 17:09
  • @swang, yes, controllerAs is definitely the first step to take, but you have to accept that you'll still have to crawl up the scope to find parents. Once you done that step then components are the way to remove the scope crawling. See http://ngmigrate.telerik.com/from-ng-controller-to-component-classes for a good summary. – Duncan Nov 23 '16 at 09:33