Search code examples
javascriptangularjsscopeangular-controller

Access parent controller n levels higher


I have a page controller, simplified here

angular.module('app').controller('MyCtrl', function () {
    this.name = 'David'
});

I'm using the controllerAs syntax in my template (MyCtrl as main) so I just use main.name in my template to access the name. All good.

In my template I have a bunch of nested directives which create new scopes and somewhere at the bottom I have an ng=controller="SubCtrl as subController". I can now access properties on the sub controller by doing subcontroller.property and within that template I can still access main.name because controllerAs is awesome.

The trouble is that in my second controller code I need to access things in the parent controller, which by this point is n levels deep. I know I can do this like this in the main controller...

$scope.main = this;

and then doing this in the second controller

var main = $scope.$parent.$parent.$parent.$parent.main;

but we all know we shouldn't do that and I won't know how many levels deep it is anyway because I won't know how many directives the developer has placed it inside that create a new scope.

I hear some people talking about creating a service for this but that sounds like it would be difficult to do generically and I don't want the users of my framework to have to create a service every time they put an include inside a tab panel, that would be crazy.

So, my current idea is to use scopes and write a function which will traverse up parent scopes until it finds the scope you specify using a comparison function. I feel there might be a better way though.


Solution

  • So why not create a service? A service is a great place to store data or access to something that is needed across more than one controller, directive, factory, whatever. Its basically a singleton, you inject in in multiple places but its always the same instance getting injected and is only created the first time it is needed. It is very easy to use and much less brittle than what you have in mind ($parent.$parent.n.....data). It is also much more testable if you are doing your unit tests.

    var module = angular.module('app');
    
    module.service('commonService', function () {
        this.sharedData = {name: ''};
    });
    
    // assume this gets loaded first
    module.controller('MyCtrl1', ['commonService', function (commonService) {
        commonService.sharedData.name = 'David'
    }]);
    
    // and this is loaded somewhere after MyCtrl2
    module.controller('MyCtrl2', ['commonService', function (commonService) {
        var name = commonService.sharedData.name;
    }]);
    

    i have not validated the syntax but this is the basic structure, very simple.