A few days back, we ran into an issue on a project using Backbone.js where we needed two Backbone views to be able to talk to one another. This is not an issue when there is a view A that has a reference to view B and binds to any events that propagate from B. Our issue was a little more in-depth because these views did not have references to each other, they only shared a model. We discovered two ways to handle this communication issue: the hack and the right way.
We have a main view rendering a preview page view as well as individual item views. The main view gives the preview view a collection of items to generate and show item previews. The previews include a title, description and comment count of the items. When a user clicks a preview view to see the full version, the main view builds the full item view and swaps it in on the page (in place of the preview page). We hold copies of the full item views and the preview view in memory to allow easy swapping between pages without needing to re-render.
Our goal was to give the comment collection to the item preview view when we pulled it from the server during the full item view creation. This would allow the preview view to stay up-to-date with comment count changes and keep the UI consistent. The only thing these view have directly in common was the item model (remember that the item previews were children of the preview view, which is a sibling of the full item views).
Initially our solutions were ugly. We could have the preview view bind to an event on the model and the full view trigger that event on the model, passing along the comment collection. This sounds reasonable, except that we are binding and triggering on a model that has no notion of this. It worked, but it felt dirty to just happen to have a common model to pass data over. More importantly, it would cause rework if we used two different models to render the full view and the preview view – suddenly there is no shared model.
The next idea was to pass the comment collection, after it loaded, up to the master view. The master view would then have to pass the collection to the preview view, which would then pass it down to the item preview view. This solution would work too, but would be a headache to implement. We had done this elsewhere, and it was time consuming. It not only took time, it also posed a risk if we were to add or remove a view in the chain – breaking the data sharing.
Backbone.js has events and event binding without a notion of a shared communication channel like an EventBus. Our final solution to our issue (surprise!) was to implement one. Below is our setup.
Simple, huh? In our application namespace we extended an empty object (with the power of underscore) to create a Backbone.Events object. This allows us to simply bind and trigger on the object from anywhere in the app.
The solution is much like Derick Bailey’s setup. Here is a look at his journey, the first post is about solving the problem and the second is a look back at the solution and how to improve it. The second post is full of great pointers on our common solution and how to get better results from it.