Sunday, 17 February 2013

Controller Inheritance in AngularJS

I was looking for abstracting common functionality into base controllers to make them reusable. I took a look at typical JavaScript Inheritance patterns and particularly liked Functional Mixins approach outlined in this article. I followed along and no surprises - everything worked fine. Only one small thing bothered me - dependency injection. A base controller might need a different set of dependencies and there was no clean way of separating dependencies for base and child controllers.

That's when I stumbled upon this gem by Misko on the Angular mailing list. The Angular $injector.invoke function mentioned in this thread does inheritance in usual JavaScript way with the addition of proper dependency injection.

Here a small plnkr to show it in action -


ParentCtrl is a typical Angular controller function but you'll notice that it's not registered as a controller. It depends on $location service while the ChildCtrl does not. ChildCtrl gets hold of injector and calls invoke on it passing the "parent" reference (ParentCtrl in this case) and its own reference as second argument. The invoke function does the magic of "mixing-in" the parent behavior into the child.

I have found this technique incredibly clean and useful so far. It helps making controllers DRYer and reusable. Hope this helps you too.

11 comments:

  1. awesome, thanks

    ReplyDelete
  2. Hey Omkar,

    Great blog. Specially for someone like me who is starting out on AngularJS. Having taken some of the initial baby steps in AngularJS, I've got someone who wants to build out a webapp in AngularJS and Play! I'm comfortable with Play! but was looking for someone to flesh out the app at the client side using AngularJS. Could you or someone you know help out in the project? Would probably need someone with your skill sets.

    Would appreciate it if you could mail me at arindam.biswas@collectivezen.com

    Thanks,

    Arindam

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Replies
    1. I'm glad it was helpful to you.

      Delete
  5. Any idea how to extend a controller thats not declared in the global scope, but with "app.controller" ?

    thanks :)

    ReplyDelete
  6. We can do this way :

    $injector.invoke(function ($controller) { $controller('CtrlName', {$scope: $scope}); });

    ReplyDelete
  7. I have verified the above method when you are trying to inherit from a controller that is declared through the module api .controller method. this in combination with regular constructor pattern objects being passed into the .controller method make for a powerful OO pattern.

    ReplyDelete
  8. Just to add a bit more, the above method does work, however if you need to do true prototypal inheritance you either have to have the objects all in the same closure/script, have them all be global, or create your own namespacing mechanism in order to be able to retrieve the constructor of your controller properly. Ben Nadel also created a factory that you can do this with. It is a bit sad that there is not a way provided by angular to get the controller constructor through it's module system directly.

    ReplyDelete