Can an AngularJS controller inherit from another controller in the same module?

89,760

Solution 1

Yes, it can but you have to use the $controller service to instantiate the controller instead:-

var app = angular.module('angularjs-starter', []);

app.controller('ParentCtrl', function($scope) {
  // I'm the sibling, but want to act as parent
});

app.controller('ChildCtrl', function($scope, $controller) {
  $controller('ParentCtrl', {$scope: $scope}); //This works
});

Solution 2

In case you are using vm controller syntax, here is my solution:

.controller("BaseGenericCtrl", function ($scope) {

    var vm = this;
    vm.reload = reload;
    vm.items = [];

    function reload() {
        // this function will come from child controller scope - RESTDataService.getItemsA
        this.getItems();
    }
})

.controller("ChildCtrl", function ($scope, $controller, RESTDataService) {
    var vm = this;
    vm.getItems = RESTDataService.getItemsA;
    angular.extend(vm, $controller('BaseGenericCtrl', {$scope: $scope}));
})

Unfortunately, you can't use $controller.call(vm, 'BaseGenericCtrl'...), to pass current context into closure (for reload()) function, hence only one solution is to use this inside inherited function in order to dynamically change context.

Solution 3

In response to the issue raised in this answer by gmontague, I have found a method to inherit a controller using $controller(), and still use the controller "as" syntax.

Firstly, use "as" syntax when you inherit calling $controller():

    app.controller('ParentCtrl', function(etc...) {
        this.foo = 'bar';
    });
    app.controller('ChildCtrl', function($scope, $controller, etc...) {
        var ctrl = $controller('ParentCtrl as parent', {etc: etc, ...});
        angular.extend(this, ctrl);

    });

Then, in HTML template, if the property is defined by parent, then use parent. to retrieve properties inherited from parent; if defined by child, then use child. to retrieve it.

    <div ng-controller="ChildCtrl as child">{{ parent.foo }}</div>

Solution 4

I think,you should use factory or service,to give accessible functions or data for both controllers.

here is similar question ---> AngularJS controller inheritance

Solution 5

Well, I did this in another way. In my case I wanted a function that apply the same functions and properties in other controllers. I liked it, except by parameters. In this way, all yours ChildCtrls need to receive $location.

var app = angular.module('angularjs-starter', []);

function BaseCtrl ($scope, $location) {
    $scope.myProp = 'Foo';
    $scope.myMethod = function bar(){ /* do magic */ };
}

app.controller('ChildCtrl', function($scope, $location) {
    BaseCtrl.call(this, $scope, $location);

    // it works
    $scope.myMethod();
});
Share:
89,760

Related videos on Youtube

Federico Elles
Author by

Federico Elles

Updated on January 29, 2020

Comments

  • Federico Elles
    Federico Elles over 4 years

    Within a module, a controller can inherit properties from an outside controller:

    var app = angular.module('angularjs-starter', []);
    
    var ParentCtrl = function ($scope, $location) {
    };
    
    app.controller('ChildCtrl', function($scope, $injector) {
      $injector.invoke(ParentCtrl, this, {$scope: $scope});
    });
    

    Example via: Dead link: http://blog.omkarpatil.com/2013/02/controller-inheritance-in-angularjs.html

    Can also a controller inside a module inherit from a sibling?

    var app = angular.module('angularjs-starter', []);
    
    app.controller('ParentCtrl ', function($scope) {
      //I'm the sibling, but want to act as parent
    });
    
    app.controller('ChildCtrl', function($scope, $injector) {
      $injector.invoke(ParentCtrl, this, {$scope: $scope}); //This does not work
    });
    

    The second code does not work since $injector.invoke requires a function as first parameter and does not find the reference to ParentCtrl.

  • gontard
    gontard over 10 years
    ParentCtrl should be a controller or is it possible to use a service ?
  • ZeissS
    ZeissS over 10 years
    @gontard: In this case it must be a controller, as $controller can only use registered controllers.
  • To Ka
    To Ka over 9 years
    It is a very good solution. Thank you. But how would I do it in case I am using Controller As syntax?
  • Salman von Abbas
    Salman von Abbas over 9 years
    @ToKa Same as other controllers? ChildCtrl as toka
  • To Ka
    To Ka over 9 years
    Yes that is one way, thanks. I came across that post when I was searching for solution. I was thinking if there was some way to load controller function and extend "this" with it.
  • Salman von Abbas
    Salman von Abbas over 9 years
    @ToKa What are you referring to by "Controller As syntax"? Show me some code.
  • Robbo_UK
    Robbo_UK over 9 years
    How is it done with controller as?.. the jsfiddle above does not include it?
  • Dan
    Dan over 9 years
    The above fiddle was asked as a question. It's worth noting that controllerAs simply assigns the controller to the scope - So you would change $scope to this (in theory)
  • opewix
    opewix over 9 years
    @DanPantry Thanks, replacing $scope to this allows usage of controller as syntax
  • Ryan Mann
    Ryan Mann almost 9 years
    This worked for me, however I'm trying to do this in a way that I have the parent controller and the child controller on the same page. This causes the $http operation in the parent controller to run twice. When the child controller injects the scope of the parent controller my $scope.AllMembers array get's populated twice as the parent controller causes it to run, then the child controller causes it to run again. Is there any way to prevent that?
  • Z2VvZ3Vp
    Z2VvZ3Vp almost 9 years
    I would like to have a universal loading variable so that when data is loading I always do the same thing, I don't think factories can do that. My parent controller can have a loading variable but the factory can't manipulate it... right?!
  • IProblemFactory
    IProblemFactory over 8 years
    For people looking for solution with var vm = this convention: stackoverflow.com/a/34113156/140070
  • herringtown
    herringtown about 8 years
    Couldn't you have just done this instead ? > $controller('BaseGenericControl', { vm: vm });
  • IProblemFactory
    IProblemFactory about 8 years
    vm is just a variable inside controller, I don't think so Angular could use it as expected.
  • gm2008
    gm2008 about 8 years
    Using "controller as" syntax has no problem. See my answer: stackoverflow.com/a/36549465/2197555
  • trueboroda
    trueboroda about 7 years
    how to be with other services not $scope that injected in the parent and chilld controllers. For instance, how can we use $routeParams in the parent controller? Is the parent's and child's services independed?
  • Nathan
    Nathan about 7 years
  • Radu Linu
    Radu Linu over 5 years
    Don't forget to declare the $controller in the ChildCtrl.