3

I'm turning a piece of a non-angular web page into an Angular version by angular.bootstrapping it with an Angular module. It works great except for the fact that it needs to communicate with the other parts of the page. Unfortunately I cannot convert the rest of the page to Angular at this time.

The best way I've figured to do this so far is by firing vanilla JS events on the bootstrapped element containing the information I need to communicate.

Are there established patterns or better ways of doing this?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Kaiden
  • 188
  • 3
  • 8
  • are u using jquery outside angular ? – levi Feb 27 '15 at 20:58
  • check this answer. http://stackoverflow.com/questions/21125332/broadcast-from-angular-to-a-jquery-listener – levi Feb 27 '15 at 21:10
  • The app is using MooTools but I have access to jQuery as well. I'd prefer not to use either and opt for a Vanilla JS solution. – Kaiden Feb 27 '15 at 22:41

3 Answers3

10

If you need to send a message from vanilla javascript to an AngularJS controller, you can access the scope like this:

var ngScope = angular.element('[ng-controller]').scope(); 
// or choose a more appropriate selector...

Then, you can do whatever you want on the scope such as broadcast an event or even just invoking a function:

ngScope.$broadcast('myEvent');
ngScope.$apply(ngScope.controllerFunction());

Sending a message from Angular to vanilla javascript should be much simpler. Simply invoke a global event or just access a global function/property.

Moshe Shaham
  • 15,448
  • 22
  • 74
  • 114
6

I had the same problem when I started to migrate the project I am working on to Angular. I had to communicate on different type of events like clicks with my non angular app. If you would like to use a known design pattern you can go with "Mediator pattern". It is a publish/subscribe based communication. You can have a singleton global instance of the mediator and access it from both angular and your non-angular app

Here's a link for the lib: http://thejacklawson.com/Mediator.js/

Before your app (both of them) loads you can do something like this:

window.mediator = new Mediator();

Later you can have in your Angular app a service like so:

angular.module()
   .service('mediator', function() {
     var mediator = window.mediator || new Mediator();
     return mediator;
 });

And from your non-angular app you can simply do:

mediator.subscribe('something', function(data){ ... });

And from your Angular controller or whatever you have you will inject the mediator service created and use it like so:

mediator.publish('something', { data: 'Some data' });

Now you have both an Angular and non-Angular way of communicating between your apps.

This solution did wonders for me.

halfer
  • 19,824
  • 17
  • 99
  • 186
Andrei CACIO
  • 2,101
  • 15
  • 28
  • This is pretty good. The reason I chose firing events on the element instead of doing this was so that I could easily instantiate multiple instances of the directive without worrying about crossing wires. How would you pub/sub per instance? – Kaiden Mar 02 '15 at 21:07
  • Well it's pretty much the same thing (if I am understanding correctly what are you trying to achieve). You can use your `directive` how many times you want. The mediator instance will be a `singletone`. The pub/sub will work on any number of instances. – Andrei CACIO Mar 03 '15 at 07:30
  • If you want tu customize the `publishing` or if you don't want all of your instances to publish data you can add an extra attribute on your argument which will be a `boolean` for example. I hope I understand correctly what did you meant by multiple instances. – Andrei CACIO Mar 03 '15 at 07:31
  • 2020 and this answer still works. Awesome! – Moses Machua Dec 13 '20 at 21:51
1

$on only listen for angular event such as $braodcast & $emit only and they will work within bootstrapped app module only.

If you want to listen jQuery events then it should use .on/.bind as like we do in jquery like $('selector').on('event',function(){});

If multiple events are associated with single DOM element then prefer Directive would be the better way get track of element event.

Directive:

app.directive('myCustomer', function() {
  return {
    restrict: 'E',
    compile: function(element,attrs){
       element.on('click focus blur',function(){
          //code here
       });
    }
  };
});

Otherwise you can bind event listener on specific element, then you can bind it by taking help of jQLite using $document, $window, $element(Provides controller level element) dependency.

Controller

$document.find('.myClass').on('click',function(e){ //placing on specific element.
   //code here
});

Plunkr Example

Hope this could help you, Thanks.

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299