1

I'm trying to figure out how controller inheritance works. I have three controllers:

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

myApp.controller('MainController', ['$scope', function($scope) {
    $scope.name = 'main';
    $scope.getName = function() {
        return $scope.name;
    };
}]);
myApp.controller('Child1', ['$scope', function($scope) {
    $scope.name = 'child1';
}]);
myApp.controller('Child2', ['$scope', function($scope) {
    $scope.name = 'child2';
}]);

and my view

<div ng-app='app'>
    <div ng-controller='MainController'>
        <div ng-bind='getName()'></div>

        <div ng-controller='Child1'>
            <div ng-bind='getName()'></div>

            <div ng-controller='Child2'>
                <div ng-bind='getName()'></div>
            </div>
        </div>
    </div>
</div>

but they're all showing "main". How do I fix this?

here's a fiddle http://jsfiddle.net/g3xzh4ov/3/

Abdul Ahmad
  • 9,673
  • 16
  • 64
  • 127

3 Answers3

2

Here's an example of how controllers can be extended in Angular.

myApp.service('baseCtrl', function () {
  this.name = 'base';
  this.getName = function() {
    return this.name;
  };
});

myApp.controller('MainController', ['baseCtrl', function (baseCtrl) {
  angular.extend(this, baseCtrl);
  this.name = 'main';
}]);
myApp.controller('Child1', ['baseCtrl', function (baseCtrl) {
  angular.extend(this, baseCtrl);
  this.name = 'child1';
}]);
myApp.controller('Child2', ['baseCtrl', function (baseCtrl) {
  angular.extend(this, baseCtrl);
  this.name = 'child2';
}]);

It obliges to use controllerAs, which replaces $scope with this, it is especially good for such cases.

Notice the usage of service instead of other Angular service types, it uses new under the hood, so this... statements can be brought right from a controller to separate service.

There are several ways of doing controller inheritance. Here is another approach.

Regarding the original code, there is no 'controller iheritance' in Angular. And $scope prototypical inheritance assumes that

$scope.getName = function() {
    return $scope.name;
};

returns $scope.name from the context where it was defined, it is MainController function in your case.

Community
  • 1
  • 1
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
1

The problem that you're facing is actually based on core Javascript functionality.

You see, the confusion that you're facing stems from the mix of scoping, and prototypical inheritance. The properties are copied over, but the scoping remains the same, preventing you from accessing the variables that you expect to be able to access. To understand this better, perhaps instead of $scope, we can look at a simpler variable:

myApp.controller('MainController', ['$scope', function($scope) {
    var a = 1;
    $scope.getName = function() {
        console.log(a); // -> 1
        console.log(b); // Error! `Uncaught ReferenceError: b is not defined`
    };
}]);
myApp.controller('Child1', ['$scope', function($scope) {
    var b = 2;
}]);

Obviously, MainController doesn't know that Child1 defined some variable called b, so it errors. That variable is strictly out of lexical scope.

Likewise, if we renamed b to a, it won't turn the value in MainController to 2. This demonstrates exactly what's happening for you: you have three things called $scope, and only one is in the lexical scope.

Two options to fix it:

1) use this:

$scope.getName = function() {
    return this.name;
};

The this solution works because of how Javascript determines "this" based on context. Basically, since it's attached to a given $scope Object, that Object's a good candidate. But Mozilla can explain this better than I can, so view their page on the topic here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

2) Otherwise, you can simply pass the $scope:

$scope.getName = function(item) {
    return item.name;
};
DRobinson
  • 4,441
  • 22
  • 31
  • 1
    It will work perfectly in his fiddle: http://jsfiddle.net/g3xzh4ov/4/ . No other changes needed. Prototypical inheritance takes care of the problem (unless you use `bind`) – DRobinson Jul 26 '15 at 20:38
0

If you want to overwrite name property in child scopes ,convert your primitive name property into object.

$scope.user = {};
$scope.user.name='main';
    $scope.getName = function() {
        return $scope.user.name;
    };

And you should read https://github.com/angular/angular.js/wiki/Understanding-Scopes for detailed information.

Mustafa ASAN
  • 3,747
  • 2
  • 23
  • 34