2

I'm creating an app where I have two apps or Divs. One is bootstrapped using ng-app directive, while the other one, I'm manually bootstrap using angular.bootstrap method.

The thing is both these divs are using a session service which $broadcasts to $rootScope, but as you can see when you the $rootScope is not shared between apps.

So a $rootScope.$broadcast in appB is not caught by appA.

Here is a demo:

angular.module('ngApp', [])
  .controller('ngAppController', function($scope, $rootScope) {
    $scope.msg = "Waiting for message..";
    $scope.$on('YO', function() {
      $scope.msg = 'Message recd';
    });

    //$rootScope.$broadcast('YO');
  });


var bElement = document.querySelector('#appB');
angular.module('appB', [])
  .run(function($rootScope) {
    $rootScope.msg = "Hello from app 'b'";
    $rootScope.$broadcast('YO');
  });
angular.bootstrap(bElement, ['appB']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="ngApp" ng-controller="ngAppController">
  <div>{{msg}}</div>
</div>
<div id="appB">
  <div>{{msg}}</div>
</div>

How can I make it so that I can catch broadcast from appB into appA?

supersan
  • 5,671
  • 3
  • 45
  • 64
  • why not just create one instance of application on common parent element of these two divs? – Stepan Suvorov May 14 '15 at 14:02
  • I'm using a template in which the header template is fixed (checks user status). The body part is another template that is loaded with PHP and it contains it's own app. But the `session` service is common between two and since the header already loads the data in `session`, I want to share the two. – supersan May 14 '15 at 14:08
  • `window.postMessage` with `addActionListeners`would help you.. – Pankaj Parkar May 14 '15 at 14:15
  • You might find [this question and answer](http://stackoverflow.com/questions/16725392/share-a-single-service-between-multiple-angular-js-apps) useful. It works by maintaining an array of `$rootScope`s on the window and notifying each when a change occurs in the service. I'm using a pattern like this for my most recent project, and it seems to work well. – Sean Walsh May 14 '15 at 14:38
  • would it make sense to keep a gobal array of `$rootScopes` inside the service and then send broadcast to each `$rootScope` individually? – supersan May 14 '15 at 15:49
  • @supersan Yep! That's exactly what's happening the first answer to the question I linked. – Sean Walsh May 14 '15 at 16:35

4 Answers4

1

I'm pretty sure angular apps are not supposed to be aware of other angular apps on the page, so for this unusual case I would suggest building a "glue" application visible to both, e.g. window.superAngularScope = {...}. You could create whatever api you require in this object. However, unless you have a really good reason for violating encapsulation between the two angular apps, I'd advise you to find a different solution.

AlexMA
  • 9,842
  • 7
  • 42
  • 64
0

I think in your case, you might need to use events outside of the angular framework, as separate angular modules don't share their rootScope.

You can use the window or document element to create event listeners and custom events, or use jQuery's trigger and on.

Yaron Schwimmer
  • 5,327
  • 5
  • 36
  • 59
0

I would suggest you to use window.addEventListener with window.postMessage which do communication between two windows or on same window.

Code

angular.module('ngApp', [])
  .controller('ngAppController', function($scope, $rootScope, $window) {
    $scope.msg = "Waiting for message..";
    $scope.$on('YO', function() {
    //  $scope.msg = 'Message recd';
    //});
    $window.addEventListener('message', function(e) {
      $scope.$apply(function(){
        $scope.msg = e.data;
      });
    });
    //$rootScope.$broadcast('YO');
  });

var bElement = document.querySelector('#appB');
angular.module('appB', [])
  .run(function($rootScope, $window) {
    $rootScope.msg = "Hello from app 'b'";
    //$rootScope.$broadcast('YO');
    $window.postMessage('YO', '*');
  });
angular.element(document).ready(function() {
  angular.bootstrap(document, ['ngApp']);
  angular.bootstrap(bElement, ['appB']);
});

Working Plunkr

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • thanks for the solution but a lot of application are already written and are using the `$scope.$on` code. Using the `message api` will require me to change a lot of code everywhere... i'm thinking it would be better to keep a global array of `$rootScope` in the session service and then send individual broadcast to each each one of them (haven't tested it yet) – supersan May 14 '15 at 15:52
  • are you sure the solution you made is work for you?? I doubt that. – Pankaj Parkar May 14 '15 at 20:17
  • hi, yes it is working.. i've posted a working demo of it below.. I basically needed the session service to broadcast to all $rootScopes and it is doing it now. – supersan May 14 '15 at 20:33
0

After some trial and error, and based on the replies I got here, this is working for me:

(function() {
  var m = angular.module('session', []);
  var serviceInstance = null;
  var rootScopeArray = [];

  m.provider('$session', function() {
    this.$get = ['$q', '$http', '$timeout',  '$window', '$rootScope',
      function($q, $http, $timeout, $window, $rootScope) {
        rootScopeArray.push($rootScope);

        if (serviceInstance) {
          return serviceInstance;
        }

        $timeout(function(){
          for (var i = 0; i < rootScopeArray.length; i++) {
            rootScopeArray[i].$broadcast("YO");
          }
        }, 2000);
        
        return serviceInstance;
      }
    ];
  });

  return m;
})();

angular.module('ngApp', ['session'])
  .controller('ngAppController', function($scope, $session) {
    $scope.msg = "Waiting for message..";
    $scope.$on('YO', function() {
      $scope.msg = 'Message recd';
    });

    //$rootScope.$broadcast('YO');
  });


var bElement = document.querySelector('#appB');
angular.module('appB', ['session'])
  .run(function($rootScope, $session) {
    $rootScope.msg = "Hello from app 'b'";
    $rootScope.$on('YO', function() {
      $rootScope.msg = 'Message recd';
    });
  });
angular.bootstrap(bElement, ['appB']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ngApp" ng-controller="ngAppController">
    <div>{{msg}}</div>
  </div>
  <div id="appB">
    <div>{{msg}}</div>
  </div>

The solution seems to be working for my app and I didn't have to change any code. Just update the session service.

If there is a better way to do it, please do advise.

supersan
  • 5,671
  • 3
  • 45
  • 64