Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><strong>Edit</strong>: The issue addressed in this answer have been resolved in angular.js <a href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#performance-improvements-44">version 1.2.7</a>. <code>$broadcast</code> now avoids bubbling over unregistered scopes and runs just as fast as $emit. <img src="https://i.stack.imgur.com/ti7Ph.png" alt="$broadcast performances are identical to $emit with angular 1.2.16"></p> <p><strong>So, now you can:</strong></p> <ul> <li>use <code>$broadcast</code> from the <code>$rootScope</code></li> <li>listen using <code>$on</code> <strong>from the local <code>$scope</code></strong> that needs to know about the event</li> </ul> <hr> <p><strong>Original Answer Below</strong></p> <p>I highly advise not to use <code>$rootScope.$broadcast</code> + <code>$scope.$on</code> but rather <code>$rootScope.$emit</code>+ <code>$rootScope.$on</code>. The former can cause serious performance problems as raised by @numan. That is because the event will bubble down through <strong>all</strong> scopes. </p> <p>However, the latter (using <code>$rootScope.$emit</code> + <code>$rootScope.$on</code>) does <strong>not</strong> suffer from this and can therefore be used as a fast communication channel!</p> <p>From the angular documentation of <code>$emit</code>:</p> <blockquote> <p>Dispatches an event name upwards through the scope hierarchy notifying the registered</p> </blockquote> <p>Since there is no scope above <code>$rootScope</code>, there is no bubbling happening. It is totally safe to use <code>$rootScope.$emit()</code>/ <code>$rootScope.$on()</code> as an EventBus.</p> <p>However, there is one gotcha when using it from within Controllers. If you directly bind to <code>$rootScope.$on()</code> from within a controller, you'll have to clean up the binding yourself when your local <code>$scope</code> gets destroyed. This is because controllers (in contrast to services) can get instantiated multiple times over the lifetime of an application which would result into bindings summing up eventually creating memory leaks all over the place :)</p> <p>To unregister, just listen on your <code>$scope</code>'s <code>$destroy</code> event and then call the function that was returned by <code>$rootScope.$on</code>.</p> <pre><code>angular .module('MyApp') .controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) { var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){ console.log('foo'); }); $scope.$on('$destroy', unbind); } ]); </code></pre> <p>I would say, that's not really an angular specific thing as it applies to other EventBus implementations as well, that you have to clean up resources.</p> <p>However, you <em>can</em> make your life easier for those cases. For instance, you could monkey patch <code>$rootScope</code> and give it a <code>$onRootScope</code> that subscribes to events emitted on the <code>$rootScope</code> but also directly cleans up the handler when the local <code>$scope</code> gets destroyed.</p> <p>The cleanest way to monkey patch the <code>$rootScope</code> to provide such <code>$onRootScope</code> method would be through a decorator (a run block will probably do it just fine as well but pssst, don't tell anybody)</p> <p>To make sure the <code>$onRootScope</code> property doesn't show up unexpected when enumerating over <code>$scope</code> we use <code>Object.defineProperty()</code> and set <code>enumerable</code> to <code>false</code>. Keep in mind that you might need an ES5 shim.</p> <pre><code>angular .module('MyApp') .config(['$provide', function($provide){ $provide.decorator('$rootScope', ['$delegate', function($delegate){ Object.defineProperty($delegate.constructor.prototype, '$onRootScope', { value: function(name, listener){ var unsubscribe = $delegate.$on(name, listener); this.$on('$destroy', unsubscribe); return unsubscribe; }, enumerable: false }); return $delegate; }]); }]); </code></pre> <p>With this method in place the controller code from above can be simplified to:</p> <pre><code>angular .module('MyApp') .controller('MyController', ['$scope', function MyController($scope) { $scope.$onRootScope('someComponent.someCrazyEvent', function(){ console.log('foo'); }); } ]); </code></pre> <p>So as a final outcome of all this I highly advise you to use <code>$rootScope.$emit</code> + <code>$scope.$onRootScope</code>.</p> <p>Btw, I'm trying to convince the angular team to address the problem within angular core. There's a discussion going on here: <a href="https://github.com/angular/angular.js/issues/4574">https://github.com/angular/angular.js/issues/4574</a></p> <p>Here is a jsperf that shows how much of a perf impact <code>$broadcast</code>brings to the table in a decent scenario with just 100 <code>$scope</code>'s.</p> <p><a href="http://jsperf.com/rootscope-emit-vs-rootscope-broadcast">http://jsperf.com/rootscope-emit-vs-rootscope-broadcast</a></p> <p><img src="https://i.stack.imgur.com/FQ9dE.png" alt="jsperf results"></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload