This article is going to discuss how we can approach controlling application flow in Sencha Touch and Ext JS applications using domain-specific events rather than relying solely on standard framework events. By following this technique you will make your applications more flexible, reusable and allow refactoring to be done with less impact.
What do I mean by controlling application flow?
When I talk about controlling application flow I am referring to the process of a user interacting with our application and kicking off some process (whether it be opening a new view or sending some data to the server) which results in other pieces of code being invoked and executed.
More often than not our application’s are built upon this principle, for example, a User clicks a button, which sends some form data to the server; a User hits the enter key on a grid row and we open a new view.
A framework-less perspective
Taking frameworks out of the equation a simple example of achieving this is shown in code below:
We use the native click
event to hook into the user’s interaction on the Save
link and kick off a process of our own. This is how it has been done since the beginning and works very well. It’s clear what has to happen, on what element, and what will happen when that situation arises.
The Sencha Way
In our Sencha applications we often use the MVC (Model-View-Controller) architecture and so listen for these interactions in our Controllers - using the control
configuration in Sencha Touch or the control
method in Ext JS. With this construct we target a component with a ComponentQuery
and define a set of name-value pairs detailing an event and the method to execute when that event happens. For example, if we take the Ext JS User form below:
We can then create a Controller that will listen for a click
event fired on the Save button within the form:
There are a couple of disadvantages with this approach:
Over-Specific Component Query
The Component Query that we have had to use to target the Save button (UserForm toolbar button[action="save"]
) is extremely specific and tied very closely to the structure of our views. OK, I’ve made this example slightly more contrived than it might be in the real world - but you get the idea!
This introduces an unnecessary dependency between our Controller and the structure of our View. If we moved our save button out of the toolbar then we would need to update the Controller to reflect this.
Tied to Event Types
We have forced the Controller into using the click
event to trigger a save. If we want to add an enter
key press to trigger the save then we need to add another handler and duplicate this code to retrieve the form values based on the context we have in that handler.
Digging into View Internals
If we are reacting to an event on the Save button, the only details we know about are that a button exists - we don’t know anything about the form that contains it and we don’t know the details of the User we are supposed to be saving. We have to retrieve those details somehow and that means we need to make assumptions about the internals of the view and the setup that currently exists.
- We assume that the button is a child of a component with an
xtype
ofUserForm
. - We assume we can get all the values to be saved using the
getValues
method.
Making these assumptions isn’t great and again makes our code fragile.
Domain-Specific Events
So, what can we do to avoid these pitfalls and improve our application?
Rather than having our application react to explicit interactions (e.g. click
, keypress
etc) we change it to react to domain-specific events which describe the action being carried out rather than the interaction that caused it. This change allows the underlying details of how it was produced to be encapsulated in the view it comes from.
To apply this principle to our User Form example we would expose a saveuser
event on the form component itself that the Controller would then listen for. This event would include the data for the User that will be saved, allowing us to concentrate on handling the save process rather than collecting everything required to do it. The updated UserForm
code can be seen below - notice the listener attached to the Save button and the two new methods - onSaveButtonClick
and doSaveUser
.
Our Controller’s code can then be rewritten as follows:
Our Controller code is a lot simpler and can focus on just performing the action - saving the user.
How do these patterns compare?
Now we’ve seen both patterns in action, how do they compare? Let’s look at the advantages of the second method:
- Our Controller code is much simpler and is given everything it needs to perform its function.
- No knowledge of the internal structure of the User Form - the ‘save’ action could be coming from anywhere and could change without this code needing to change.
There is a downside to the approach of using domain-specific events and that is that more logic is moved to the View. We are always told to keep logic out of the views but I’m of the opinion that as long as it’s ‘view’ logic and not ‘business’ logic then it can belong here.