39

I am working on an AngularJs project. I have a service which sets and removes events on some buttons. This service is utilized by another service which I do not want to interact directly with the buttons. However I would like a button click event to be filtered up through the first service and handled in the second one. Since I don't want the second service to be aware of the buttons, I figure I will need to create a custom event in the first service. How can I create a custom event and fire it when a button is clicked?

Thanks in advance.

1
  • Maybe the observer pattern bring other solutions to the problem scotch.io/bar-talk/… Commented Dec 21, 2016 at 21:23

3 Answers 3

86

If you want to send an event between services/directives use broadcast:

$rootScope.$broadcast('buttonPressedEvent');

And recieve it like this:

$rootScope.$on('buttonPressedEvent', function () {
             //do stuff
        })
Sign up to request clarification or add additional context in comments.

3 Comments

Hey, thank you very much. However is there for the service itself to fire the event such so that in order to handle it the second service must reference the first? For example: inside service2 there would be a line of code: service1.eventfired = do_something().
Yes, that is the last part from my answer. in your second service you need to add that code, just change $scope.$on to $rootScope.$on
This is so much cleaner than what I was considering. Hooray for you, and hooray for $rootscope.$broadcast()!
35

Any global scope based integration would hurt namespacing and could result terrible side-effects in middle and large size applications. I recommend a bit more complicated but definitely cleaner solution, using proxy service.

1. Create proxy service

angular.module('app').service('userClickingHelper', [
  function() {
    var listeners = [];
    return {
      click: function(args) {
        listeners.forEach(function(cb) {
          cb(args);
        });
      },
      register: function(callback) {
        listeners.push(callback);
      }
    };
  }
]);

2. Create button directive which is using the userClickingHelper as a dependency.

angular.module('app').directive('onDistributedClick', ['userClickingHelper', function (userClickingHelper) {
    return {
        restrict: 'A',
        link: function (scope, element) {
            element.on('click', userClickingHelper.click);
        }
    };
}]);

3. Register your unrelated service with using the proxy.

angular.module('app').service('myUnrelatedService', [function (userClickingHelper) {
    userClickingHelper.register(function(){
        console.log("The button is clicked somewhere");
    });
}]);

The result is an isolated scope of event distribution, yet neither the directive, nor the service knows about each other. It is also easier to unit-test.

I am using similar solution for globally accessable "Loading" modal so I can abstract the way a controller/service is saying "user should not click or anything right now".

2 Comments

+1. This answer provides general good practice when implementing listeners / handlers whereas the answer by Chancho is more tailored to the OP's use case.
I liked this approach as we can hook for notification from trusted source or we know from where it should come from. If we use $rootScope.$broadcast then in complex application there are high chances that someone will overlap on same event to broadcast their data.
8

You can create and emit an event with the following on your element

ng-click="$emit('customEvent')"

Then in your controller you can use

$rootScope.$on('customEvent', function(){
    // do something
})

Demo

4 Comments

I wish for two 'services' to communicate not a view and a controller. However after reading your answer I got a sort of epiphany. I could just establish a two-way communication between the services. When service 1 captures the button click event it can call the required method in service two which will do the processing I require. It seems like a simple way of achieving what I need. Thank you very much!
Scratch that! I would be tightly coupling the two services together! I will use the $rootScope.$on() and $rootScope.$broadcast() method indicated by another user. Thanks again! (And in case you are wondering, yes, I am a novice)!
it actually does exactly the same, my demo has broadcast and emit functions, I used emit for demonstration, my approach was to emit directly from the DOM rather than adding more logic in the controller
This answer needs more upvotes! For my needs, $rootScope.$broadcast() is more appropriate, but I can see how in some situations, using ng-click="$emit('customEvent')" would be better.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.