ExtJS 6: Getting your Controllers to talk to one another.
Like most new versions of a programming library, version 6 of ExtJS has brought with it a number of changes for users coming from Sencha Touch. Perhaps the biggest of these changes is the switch from an MVC (Model — View — Controller) architecture to an MVVM (Model — View — ViewModel) architecture.
In the old MVC world, a set of common views would be grouped under a single controller. This allowed for easy communications between these shared views using the common controller. However, in the switch over to an MVVM architecture, each view has its own separate controller, as well as its own ViewModel, which governs the data for an individual view.
This separation is great for creating discrete chunks of self-contained code that can be reused in multiple applications. For anyone who has created and recreated code for system users (login, profiles, lists of users, etc.), this can be a godsend. If each View — Controller — ViewModel is a self-contained piece, it’s trivial to copy the code to a new application.
But every so often, you need to have your views talk to each other. Maybe you edited a user in the User Profile view and you need to notify your main view so it can update the user’s avatar. We could, within the User Profile’s ViewController, use an Ext.ComponentQuery to find the main view and update it, but that means that if we ever updated our main view, we’d have to remember to fix our User Profile ViewController as well.
In the MVC world we could have one controller that was responsible for listening to the User Profile for changes, then updating the appropriate components elsewhere in the application, and that’s definitely one solution for your MVVM application as well: create an application-level controller to listen for cross-view events. But what if the view you need to listen to, or that you need to modify, hasn’t been created yet? Your app controller needs to be aware of what’s there and what isn’t.
Meet the Mediator Object
A third, and potentially better option, is to use a Mediator object. A Mediator is essentially a third party whose sole purpose is to pass events around between objects that emit them and objects that need to listen for them. An object that uses the Observable mixin works great for this:
Now you just require this in your view controllers and either fire events or listen for them:
Since the Mediator object always exists, you can add listeners or fire events at any time, and only ViewControllers that are currently active will be affected.
Now when our UserProfileViewController’s onUserUpdated function is called, we notify the Mediator object that we’ve done a user update. Any other Controllers that are listening for that event get sent the updated user data. And our User Profile view doesn’t need any knowledge of what they are or what they’re doing with the data.
A step further
I’m a big fan of removing as much boilerplate code as possible. So even though the Ext.ux.Mediator object is a good solution to our problem, we can go one better. Why not make it a mixin and have it read configurations from our controllers, rather than having to write code into each controller for sending and receiving these events?
My code for the Mediator mixin is up on GitHub, but here’s an example of how to modify the previous two controllers to use it:
You see that we have three files now. The first is called “AbstractController” and just includes the mixin. That way, if I always extend my AbstractController rather than Ext.app.ViewController, I know that I’ll always have the right set of mixins for all of my ViewControllers.
In the MainViewController we’ve removed the init function and added a “subscribe” config. Now when the mediator fires the “userupdate” event, our main controller will call its updateUserAvatar function.
In our UserProfileViewController, we now call “this.publish” rather than the much longer Ext.ux.Mediator.fireEvent. This is a handy shorthand function added by the Mediator mixin that works exactly the same as fireEvent.
So you can see from these examples that communication between separate ViewControllers can be easily established without traveling too far outside the “Official Sencha Way of Doing Things.”
By combining a Mediator object and an abstract controller, you can quickly add communication between your controllers. After that, it’s just a simple matter of publish and subscribe to get the different view controllers talking.
Now you can get back to coding the next big thing.