Warning: contains foul language

I hate it. It is so common, that it is even the most accepted solution on StackOverflow. My hate rage is one of the main reasons why I write this little piece of advice. Let’s look at very very common piece of angular controller code.

$rootScope.$broadcast('ohSomethingChanged', whatever);

$rootScope.$emit('ohSomethingElse', whatever); //Then in other controller

$scope.$on('ohSomethingElse', function(whatever){

$scope.myRedundantState = whatever *2;

});

Especially coming from Backbone, developers tend to love $broadcast. It feels natural to them, because in Backbone, there is no other way of triggering a render on another Backbone controller. In Angular, we are free from doing this manual chore. Yes, it comes at a cost of digest loops, but it is a price I gladly pay. And hey, if you chose Angular for your app, you already paid too.

By using this pub/sub pattern to change state of another controller(most common use case) in Angular, you are basically saying:

fuck you Angular and your digest loop, I will just change the state myself

If you are one of those developers who do this and cannot be swayed, just use React. Your fear of the digest loop actually severely limits your change propagation options. And frankly, your React code will perform better.

Recurring argument I hear in favour of the pub/sub is that it removes tight coupling. People who say this are mostly coming from type-checked languages, where it is not trivial to just pass one object instead of another. Are they right? In a dynamic language such as JavaScript, what is the difference between calling a method on an object and invoking an event? An object is just a hash of values. Event listening is just a hash of arrays of functions. These arrays can be empty or not.

So while true that, you remove explicit contract by utilising pub/sub, it actually makes more sense to leave to contract explicitly defined.

Imagine you start refactoring code which propagates all changes via $broadcasts. What happens when you run your app in the middle of refactoring changes? Nothing. App will not work and you will get hardly any errors in the console.

Why? Because there is no ‘coupling’.

Trying the same with a ‘tightly coupled’ app written without $broadcasts you’ll notice it breaks and throws error for each line not yet refactored.

Which would you prefer? I know I would take the errors every day. At least I know what I have to fix, even if I don’t have unit tests.

Funny is that developers advocating for $broadcast usually forget to unsubscribe from the event on the $rootScope when the controller is destroyed. If that controller is reinstantiated, it leaks. Pub/sub always has been a leaky abstraction. With Angular.js, if you are new to the framework, it might not be obvious to you when and how often your controllers are instantiated. Be careful if you’re not 100% sure that your controller is called only once.

Solutions

Instead of listening to a broadcast and changing state upon callback being triggered, there are two options:

Make it stateless — use a filter or a getter function on scope. Use that in your template. $watch-sometimes you need to do an AJAX call or other asynchronous call in reaction to other change. WIth $watch, Angular is responsible for triggering an event-other parts of your app just subscribe to a change. Angular’s job is triggering that callback and it does it reliably well thanks to the digest loop. Your subscriber controller is solely responsible for it’s features.

This is true decoupling, which will allow you to have drop in Angular web components. Say no to tangled web of matching event names.

Considering that you use one time bindings(and that is quite a big IF), than yes, both of these are slower than $broadcast. Possible performance penalty is well worth it, because your code will be much shorter, easier to unit test, much nicer to read and debug.

So please, fuck the user with IE8 on old Pentium 3 laptop, help the poor sodd who will maintain your code in the future. Make it stateless, remove state where possible, make it truly decoupled, but define contracts between pieces of code. The possibility of breaking this contract is not a bad thing. Avoid $broadcast/$emit unless you really have and edge case.