Sencha Touch

Mobile applications are everywhere these days and the number of development frameworks geared towards mobile audiences is growing as well. This can make it difficult for us developers who have to spend valuable training time learning one or more of these frameworks in order to be able to do our jobs. Sencha Touch is one of these frameworks and it allows a developer to code in HTML, CSS, and Javascript, and then package up the application to run on different mobile devices. This article is intended to provide a general feel for what Sencha Touch is and what it does.

Getting Started

Sencha Touch provides a utility to help developers get started with a new application. It includes a command line executable that will do everything from generating a new empty application shell to performing app builds for you. This is very useful since you will not need to spend time making sure that the file system was set up properly or creating all of the required files. This is also really important because Sencha Touch is fairly rigid in its expectations for what the file names are and where they are located. The framework is very opinionated, which means that it will prescribe certain ways to do things and you are required to do them that way. For instance, if you have a view named “Test” and you want to create a controller for that view then the controller must be also named “Test” and must be placed in a specific folder location.

The same tool also provides the ability to perform some optimizations for your production deployment. The CSS for each component is defined separately so when you go perform a build you can configure it to not include any components that are not used in the application. This helps to reduce the size of the application. This is fairly important since the entire application, including JavaScript, HTML, and CSS, is stored in the browser’s local storage in order to enable offline access. Since there is a 5MB limit on local storage the application needs to be kept as small as possible.

The Basics

Sencha Touch is built around the concept of components and is very configuration based. The user interfaces are called “views” and the developer adds and configures different components in the views. If a component needs to handle events then the developer can create a controller for that view and wire up event handlers. The framework also provides functionality to help when working with data, whether stored locally or on a remote server. While it does not have direct two-way data-binding like KnockoutJS or AngularJS, there is a method for mimicking that functionality.

The structure of the different Sencha Touch files is very similar. Here is a sample of what a file looks like.

Ext.define('TodoItems.view.Main', {
    extend: 'Ext.tab.Panel',
    xtype: 'main',
    requires: [
        'Ext.TitleBar',
        'TodoItems.view.AddItem',
        'TodoItems.view.TodoItems'
    ],
    config: {
        tabBarPosition: 'bottom',

        items: [
            {
                title: 'To-Do Items',
                iconCls: 'list',
                xtype: 'listview'
            },
            {
                title: 'Add Items',
                iconCls: 'compose',
                xtype: 'additem'
            }
        ]
    }
});

There are several important things to take note of in this code example. The structure of these files is similar to an AMD or RequireJS module. Each file is given a unique name, in this case “TodoItems.view.Main”. This name is very strict in what it can be and in this example it signifies that the application name is “TodoItems”, and this file resides in the “view” folder with a file name of “Main.js”. Unlike an AMD module, instead of defining a callback function Sencha Touch files define a javascript object. This object includes the configuration data necessary for that file.

The “xtype” is also an important thing to take note of. The xtype can be thought of as an alias for the file, and most references to this specific view will actually use the xtype instead of the full name. One benefit of this is that if you refactor this file and move it somewhere else you will not have to update all of the references to it since the xtype will still be the same even though the name is not.

The “extend” property tells Sencha Touch what type of component or object this specific file is extending. In this case we are creating a tab panel component in the UI.

The “requires” property tells the framework what other files in the application are needed for this file. This is useful when you consider that Sencha Touch has dozens of components available out of the box, and if your application only uses a handful of them then it would be pointless to bundle all of the components in the production version. The build tool is smart enough to go through all of the files and remove the pieces that are not needed by the application for the production version.

The last thing to note is the “config” section. Like it sounds, this is where the current component is configured. The purpose of this section varies depending on what type of component you are creating.

Views

Views are where the user interfaces are defined. Even though views are javascript files the contents are mainly configuration for the various components present in the view and virtually no javascript logic will be present in these files. If a view requires more complicated logic like event handlers then a controller can be created to handle those. The config section for a view defines how the current component renders, and can also define any nested items. In the example above, since we are creating a tab panel view the configuration element is set to have the tabs display at the bottom of the screen with two items on it. When one of those items is clicked on then the xtype referred to by that item will become visible in the panel itself.

Controllers

Controllers allow for the developer to create specific interactions with the views. Here is a sample controller.

Ext.define('TodoItems.controller.Form', {
  extend: 'Ext.app.Controller',
  config: {
    refs: {
    	'addButton': 'itemform #btnAdd',
    	'itemForm': 'itemform',
    	'mainView': 'main'
    },
    control: {
    	'addButton': {
    		tap: 'onAddTap'
    	}
    }
  },
  onAddTap: function(b,e) {
    	errorString = "";
    	// Construct the data model for our todo item
    	var model = Ext.create('TodoItems.model.TodoItem');

    	// Populate the data item from the form fields
    	this.getItemForm().updateRecord(model);
    	model.set('addDate', new Date().toString());

    	// Make sure that the model validation passes
    	var errors = model.validate();
    	if (!errors.isValid()) {
    		errors.each(function (errorObj){
			    errorString += errorObj.getMessage() + "<br />"
			  });
			  Ext.Msg.alert('Errors in your input',errorString);
		  } else {
		  	model.save();
		  	this.getItemForm().reset();
		  	Ext.getStore('TodoItems').load();
		  	this.getMainView().setActiveItem(0);
        }
    }
});

Another nice feature with Sencha Touch is that it attempts, and strongly suggests, to prevent the developer from interacting directly with the DOM. The “refs” section is a place where the developer can create references to DOM elements, but it is abstracted through the framework so that the developer is not directly interacting with the elements. Any element referenced in the “refs” section is given a dedicated getter on the controller object. For instance, this examples defines a ref for “itemForm”, which means that an event handler can call “this.getItemForm()” to access the element.

Event handlers for elements registered in the “refs” section are added to the “control” section. The event handlers themselves are probably the only part of Sencha Touch that a normal web developer would consider being normal JavaScript code. These handlers are the places where you can explicitly interact with other Sencha components or manipulate application state.

While Sencha Touch does not have built-in two way data-binding it does have some helper functions to mimic that ability. In the example above, creating a model using Ext.create will initialize an empty data model and then the call to “this.getItemForm().updateRecord(model)” hydrates the model’s properties from any matching form fields. The same feature exists for taking a populated model and setting form values based on the model’s values.

Working with Data

Data interaction is provided through models, proxies, and stores. Models define the individual data models and any necessary validation or constraints on the fields, stores are collections of one or more models, and proxies connect the stores or models to the data source, whether local or remote. Here is an example of how the three fit together.

Ext.define('TodoItems.model.TodoItem', {
 extend : 'Ext.data.Model',
 requires: ['Ext.data.proxy.LocalStorage','Ext.data.identifier.Uuid'],
    config : {
        idProperty: 'id',
        identifier: 'uuid',
        fields : [
          {
            name: 'id'
          },
          {
   	      {
            name : 'addDate',
            type : 'Ext.data.Types.Date',
            dateFormat : 'mm/dd/YYYY'
          },
          {
            name: 'completed',
            type: 'boolean',
            defaultValue: false
          }
        ],
        validations: [{
                field: 'addDate',
                type: 'presence',
                message: 'Item Add Date is required.'
            }],
        proxy : {
            type : 'localstorage',
            id : 'todoItem-proxy'
        }
    }
});
Ext.define('TodoItems.store.TodoItems', {
 extend: 'Ext.data.Store',
 requires: ['TodoItems.model.TodoItem'],
 config: {
  model: 'TodoItems.model.TodoItem',
  remoteFilter: false,
  remoteSort: false,
  autoLoad: true,
  sorters: [
    { property : 'NAME', direction: 'ASC' }
  ],
  grouper: {
  	groupFn: function (item) {
  		if (item.get('completed')) {
  			return "Completed";
  		} else {
  			return "In Progress";
  		}
    }
  }
 }});

In this case the proxy configuration was added to the model instead of the store, and the reason for that is if the model is connected to the proxy then it allows you to do something like “model.save()” to automatically sync changes to the model back to the actual data source. Stores have the ability to filter and sort the data, but if the data set is too large or complex then it can also be configured to make a network request and have the external data source perform the filtering and sorting. Stores also support grouping items based on values from the models.

Conclusion

Sencha Touch does have some negatives, specifically its performance due to the non-compiled nature of the code, the proprietary nature of the framework, and the somewhat steeper learning curve. However, it does have some positives to counteract those. It provides developers with a way to target multiple mobile devices with a single code base and depending on your business case this can be more than worth it. Even if you do not have any specific projects in mind for it, I think that it would be a good idea to learn at least the basics of Sencha Touch to have it as an option. I hope this has provided at least a basic overview of Sencha Touch and how some of the pieces fit together.

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: