Search code examples
javascriptangularjssiblings

Can Siblings Controllers communicate with each other without the help of the Parent - AngularJS


I'm working on a small app in AngularJS. My project contain a Body.html file that contain 3 views: SideMenu, Header and Content, each with each own Controller and a MainController as there parent - the controller of the Body.html.
Can the header's controller change a property in the side-menu - the open/close status of the side-menu. And Can the side-menu controller change a property in the header - the header's text.
I can use the main controller, since both of the header's controller and the side-menu controller can reference the main controller. But the data won't be consist. Updating the data from the 1st controller wan't effect the data in the 2nd controller (without the use of $watch).

Can both the side-menu's controller and the header's controller (sibling controllers) communicate with each other? without the help of there parent?


Body.html

 <div>
     <!-- Header placeholder -->
     <div ui-view="header"></div>

     <!-- SideMenu placeholder -->
     <div ui-view="sideMenu"></div>

     <!-- Content placeholder -->
     <div ui-view></div>
 </div>

Header.html

 <div>
    {{ headerCtrl.text }}
 </div>
 <div ng-click="headerCtrl.openSideMenu()">
    --Open--
 </div>

HeaderController.js

 // sideMenuCtrl = ???
 headerCtrl.text = "Title";
 headerCtrl.openSideMenu = function()
 {
    sideMenuCtrl.isSideMenuOpen = true;
 };

SideMenu.html

 <div ng-class={ 'side-menu-open': sideMenuCtrl.isSideMenuOpen }>

     <div ng-repeat="menuItem in sideMenuCtrl.sideMenuItems"
          ng-click="sideMenuCtrl.selectMenuItem(menuItem)">
          {{ menuItem.text }}
     </div>
 </div>

SideMenuController.js

 // headerCtrl = ???
 sideMenuCtrl.selectMenuItem = function(menuItem)
 {
    headerCtrl.text = menuItem.text;
 }

Solution

  • As stated in my comment, you can use an AngularJS service to share some data between your controllers.

    app.service('AppContextService', function(){
        this.context = {
            isSideMenuOpen: false
        };
    });
    
    app.controller('SideMenuCtrl', ['$scope', 'AppContextService', function($scope, AppContextService) {
        // exposing the application context object to the controller.
        $scope.appContext = AppContextService.context;
    }]);
    
    app.controller('HeaderCtrl', ['$scope', 'AppContextService', function($scope, AppContextService) {
        $scope.appContext = AppContextService.context;
        $scope.openSideMenu = function()
        {
            $scope.appContext.isSideMenuOpen = true;
         };
    }]);
    

    Then adapt the HTML to use your shared appContext object.

    <div ng-class={ 'side-menu-open': appContext.isSideMenuOpen }>
       [...]
    </div>
    

    Here is a working fiddle that illustrates the issue: fiddle

    This answer covers the use of a service to fit your needs but I am sure that there are other (and perhaps better) ways to tackle the problem which might involve other Angular feature, or even some overall application refactoring.

    To dig a little deeper, this SO topic might be a good start: difference between service, factory and providers