Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm trying to set up some nested views in angularjs. I've been using the ui-router library to do this which works great for the most part. The problem is that there is a separate controller for each view with no real inheritance going on between them. If I want to modify something in a parent controller from a child controller I have to use $scope.$parent . This is a bit of a pain and it can become worse if there are multiple levels of inheritance and you have to remember which level the variable you are accessing is on. Also if you forget to use $parent in your child controller and you try to modify one of the parent's variables, Angular will create a new instance of the variable which could lead to some hard to track down bugs.

Ideally I would just be able to use prototype inheritance. This would also map nicely into classes in Typescript or Coffeescript. One way I thought of to do this would be to get rid of all the parent controllers and just have the child controllers which would inherit any common functionality from prototypes (super classes). Then you would just have to throw the controller up on the $rootScope so that the parent views could access it.

Can anyone think of any issues with this solution or better solutions? Would I be better off just using $parent and letting Angular handel the "inheritance".

Thanks

share|improve this question
add comment

2 Answers

You should be able to enable protypal inheritance if you modify the source slightly.

Open the file ./ui-router/src/viewDirective.js and you should find this near the top:

var directive = {
    restrict: 'ECA',
    terminal: true,
    transclude: true,
    ...

Add another line such that it reads:

var directive = {
    restrict: 'ECA',
    terminal: true,
    transclude: true,
    scope: true,
    ...

This should enable inheritance. I can't find a good direct source on this, but this you tube playlist is full of all sorts of angular goodness.

share|improve this answer
    
This worked although it does not work very well the the "controller as" syntax. for example if in my module configuration I have 'controller: "myChildCtrl as ctrl"' then I can't access the parent scope through 'this'. e.g. this would not work 'this.someParentVariable = 3'. However this would work '$scope.ctrl.someParentVariable = 3'. –  rob Aug 28 '13 at 20:31
2  
There's some confusion as to how the inheritance works. $scopes are objects used by controllers and these inherit from eachother -- not the controller. When a controller is placed on a DOM element, the $scope on that element generally inherits from the $scopes on ancestor DOM elements and the controllers don't have anything to do with this directly. As far as I can tell, using the "controller as ctrl" syntax actually puts a reference to the controller on the current scope. This is the same object that you would reference inside of the controller's constructor function as "this". –  John Henry Aug 29 '13 at 0:43
    
"controller as" syntax is considered harmful by some (me included). You can do prototypal inheritance as mentioned above if you want to controller instances to have common functionality or instance variables, but two instances will still not share the same scope. You should use a service to share data between controllers instances. –  stu.salsbury Sep 1 '13 at 16:48
add comment
up vote 0 down vote accepted

As John pointed out the $scope objects inherit from each other but the actual controllers do not. So I decided to set it up so that the controllers do inherit from each other. To do this I have one root controller that just looks like this:

function rootCtrl($scope) {
    $scope.ctrl={};
    $scope.ctrl.scope = $scope;
}

Then in my most deeply nested child controllers I have something like this

function myCtrl($scope, $dependency1, $dependency2) {
    myCtrlImpl.apply($scope.ctrl, [$http, $dependency1, $dependency2]);
}

function myCtrlImpl($dependency1, $dependency2) {
    this.someVariableThatIsAccessableEverywhere = ":)";

    //If I want to access scope from here I can just do something like
    //this.scope.$watch...
}

Now if I want to move any functionality from myCtrlImpl into a base prototype, I can just use standard prototype inheritance.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.