How do I create a custom event in an AngularJs service

60,898

Solution 1

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
        })

Solution 2

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".

Solution 3

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

Share:
60,898

Related videos on Youtube

rmg.n3t
Author by

rmg.n3t

Updated on July 09, 2022

Comments

  • rmg.n3t
    rmg.n3t almost 2 years

    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.

    • William Ardila
      William Ardila over 7 years
      Maybe the observer pattern bring other solutions to the problem scotch.io/bar-talk/…
  • rmg.n3t
    rmg.n3t almost 10 years
    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!
  • rmg.n3t
    rmg.n3t almost 10 years
    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)!
  • rmg.n3t
    rmg.n3t almost 10 years
    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().
  • Chancho
    Chancho almost 10 years
    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
  • Jonathan de M.
    Jonathan de M. almost 10 years
    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
  • TommyAutoMagically
    TommyAutoMagically over 7 years
    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.
  • TommyAutoMagically
    TommyAutoMagically over 7 years
    This is so much cleaner than what I was considering. Hooray for you, and hooray for $rootscope.$broadcast()!
  • Yatit Thakker
    Yatit Thakker over 6 years
    +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.
  • Aamol
    Aamol over 5 years
    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.