Working with $scope.$emit and .$on

How can I send my $scope object from one controller to another using .$emit and .$on methods?

function firstCtrl($scope)
{
    $scope.$emit('someEvent', [1,2,3]);
}

function secondCtrl($scope)
{
    $scope.$on('someEvent', function(mass) { console.log(mass); });
}

It doesn't work the way I think it should. How do $emit and $on work?

First of all, parent-child scope relation does matter. You have two possibilities to emit some event:

  • $broadcast -- dispatches the event downwards to all child scopes,
  • $emit -- dispatches the event upwards through the scope hierarchy.

I don't know anything about your controllers (scopes) relation, but there are several options:

  1. If scope of firstCtrl is parent of the secondCtrl scope, your code should work by replacing $emit by $broadcast in firstCtrl:

    function firstCtrl($scope)
    {
        $scope.$broadcast('someEvent', [1,2,3]);
    }
    
    function secondCtrl($scope)
    {
        $scope.$on('someEvent', function(event, mass) { console.log(mass); });
    }
    
  2. In case there is no parent-child relation between your scopes you can inject $rootScope into the controller and broadcast the event to all child scopes (i.e. also secondCtrl).

    function firstCtrl($rootScope)
    {
        $rootScope.$broadcast('someEvent', [1,2,3]);
    }
    
  3. Finally, when you need to dispatch the event from child controller to scopes upwards you can use $scope.$emit. If scope of firstCtrl is parent of the secondCtrl scope:

    function firstCtrl($scope)
    {
        $scope.$on('someEvent', function(event, data) { console.log(data); });
    }
    
    function secondCtrl($scope)
    {
        $scope.$emit('someEvent', [1,2,3]);
    }
    

I would additionally suggest a 4th option as a better alternative to the proposed options by @zbynour.

Use $rootScope.$emit rather than $rootScope.$broadcast regardless of the relationship between trasmitting and receiving controller. That way, the event remains within the set of $rootScope.$$listeners whereas with $rootScope.$broadcast the event propagates to all children scopes, most of which will probably not be listeners of that event anyway. And of course in the receiving controller's end you just use $rootScope.$on.

How can I send my $scope object from one controller to another using .$emit and .$on methods?

You can send any object you want within the hierarchy of your app, including $scope.

Here is a quick idea about how broadcast and emit work.

Notice the nodes below; all nested within node 3. You use broadcast and emit when you have this scenario.

               3
           --------
          |        |
         ---     ----
        1   |    2   |
      ---  ---  --- ---
      |  | |  | | | | |

Check out this tree. How do you answer the following questions? note: There are other ways, but here we'll discuss broadcast and emit. Also, when reading below text assume each number has it's own file (directive, controller) e.x. one.js, two.js, three.js.

How does 1 speak to 3? In file one.js

scope.$emit('messageOne', someValue(s));

In file three.js

scope.$on('messageOne', someValue(s));

How does 2 speak to 3?

In file two.js

scope.$emit('messageTwo', someValue(s));

In file three.js

scope.$on('messageTwo', someValue(s));

How does 3 speak to 1 and/or 2?

In file three.js

scope.$broadcast('messageThree', someValue(s));

In file one.js && two.js whichever file you want to catch the message.

scope.$on('messageThree', someValue(s));

How does 2 speak to 1?

In file two.js

scope.$emit('messageTwo', someValue(s));

In file three.js

scope.$on('messageTwo', function( event, data ){
  scope.$broadcast( 'messageTwo', data );
});

In file one.js

scope.$on('messageTwo', someValue(s));

HOWEVER

When you have all these nested child nodes trying to communicate like this, you will quickly see many $on's $broadcast's, and $emit's. Here is what I like to do.

In PARENT NODE ( three in this case... ), which may be your parent controller...

So, in file three.js

scope.$on('pushChangesToAllNodes', function( event, message ){
  scope.$broadcast( message.name, message.data );
});

Now in any of the child nodes you only need to $emit the message or catch it using $on.

NOTE: It is normally quite easy to cross talk in one nested path without using $emit, $broadcast, or $on, which means most use cases are for when you are trying to get 1 to communicate with 2 or vice versa.

How does 2 speak to 1?

In file two.js

scope.$emit('pushChangesToAllNodes', sendNewChanges());

function sendNewChanges(){ // for some event.
  return { name: 'talkToOne', data: [1,2,3] };
}

In file three.js

We already handled this one remember? In file one.js

scope.$on('talkToOne', function( event, arrayOfNumbers ){
  arrayOfNumbers.forEach(function(number){
    console.log(number);
  });
});

You will still need to use $on with each specific value you want to catch, but now you can create whatever you like in any of the nodes without having to worry about how to get the message across the parent gap.

Hope this helps...

You can call a service from your controller that returns a promise and then use it in your controller. And further use $emit or $broadcast to inform other controllers about it. In my case, I had to make http calls through my service, so I did something like this :

function ParentController($scope, testService) {
testService.getList()
.then(function(data){
      $scope.list = testService.list;
      })
.finally(function(){
       $scope.$emit('listFetched');
           })



 function ChildController($scope, testService){
 $scope.$on('listFetched', function(event, data) {
   // use the data accordingly
    })
 }

and my service looks like this

app.service('testService',['$http', function($http){

this.list = [];

 this.getList = function () {
    return $http.get(someUrl)
        .then(function (response) {
            if (typeof response.data === 'object') {
                 list = response.data.results;

                return response.data;
            } else {
                // invalid response
                return $q.reject(response.data);
            }

        }, function (response) {
            // something went wrong
            return $q.reject(response.data);
        });

       }


    }])

This is my function:

$rootScope.$emit('setTitle', newVal.full_name);

$rootScope.$on('setTitle', function(event, title) {
    if (scope.item) 
        scope.item.name = title;
    else 
        scope.item = {name: title};
});

Below code shows the two sub-controllers from where the events are dispatched upwards to parent controller (rootScope)

<body ng-app="App">



  <div ng-controller="parentCtrl">

    <p>City : {{city}} </p>
     <p> Address : {{address}} </p>

     <div ng-controller="subCtrlOne">
        <input type="text" ng-model="city"/>
        <button ng-click="getCity(city)">City !!!</button>
       </div>

     <div ng-controller="subCtrlTwo">

        <input type="text" ng-model="address"/>
        <button ng-click="getAddrress(address)">Address !!!</button>

    </div>





     </div>

</body> 

     var App = angular.module('App',[]);

 //parent controller
  App.controller('parentCtrl',parentCtrl);

     parentCtrl.$inject = ["$scope"];

       function parentCtrl($scope) {

         $scope.$on('cityBoom',function(events,data){
            $scope.city = data;
     });

         $scope.$on('addrBoom',function(events,data){
            $scope.address = data;
     });

  }


  //sub controller one

  App.controller('subCtrlOne',subCtrlOne);

   subCtrlOne.$inject =['$scope'];

      function subCtrlOne($scope) {

         $scope.getCity=function(city){

         $scope.$emit('cityBoom',city);

        }
     }


//sub controller two

  App.controller('subCtrlTwo',subCtrlTwo);

    subCtrlTwo.$inject = ["$scope"];

        function subCtrlTwo($scope) {

             $scope.getAddrress = function(addr) {

               $scope.$emit('addrBoom',addr);

      }

  }

http://jsfiddle.net/shushanthp/zp6v0rut/

<!DOCTYPE html>
<html>

<head>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script>
var app = angular.module('MyApp',[]);
app.controller('parentCtrl',function($scope){
  $scope.$on('MyEvent',function(event,data){    
    $scope.myData = data;
  });
 });

app.controller('childCtrl',function($scope){
  $scope.fireEvent = function(){ 
  $scope.$emit('MyEvent','Any Data');
  }  
 });
</script>
</head>
<body ng-app="MyApp">
<div ng-controller="parentCtrl" ng-model="myName">

{{myData}}

 <div ng-controller="childCtrl">
   <button ng-click="fireEvent()">Fire Event</button>
 </div>

</div>
</body>
</html>