Building a Mobile Web App using Sencha Touch 2 and MVC

By March 15, 2012 Mobile One Comment

In my previous post, I showed how to build a mobile web app using jQuery Mobile. Its use of HTML5 data attributes and CSS classes made it pretty straightforward, especially if you are used to building regular websites. The architecture of your mobile website would have to be based on the server-side though, whether this be ASP.NET MVC, PHP, Ruby, etc. Then I came across Sencha Touch which changes this mentality to a more progressive approach… client-side applications!

First let me give you a bit of background about Sencha. These are the guys who made Ext JS, a Javascript framework that includes object-oriented programming, UI components, and an available MVC pattern (yes, this is all Javascript!). This very same framework is the core foundation for their mobile web framework called “Touch”. I had to mention this because now you see how the architecture will be moved from server-side to client-side.

Setup the Project

Let us start a skeleton project so we can build our mobile web application. Normally I would tell you to fire up Visual Studio. Not this time. We need an IDE geared more towards Javascript and HTML5. I saved you the trouble and found that JetBrains WebStorm is the BEST Javascript IDE! I tried Komodo (lots of features), Coda, MacRabbit’s Expresso (so pretty), Aptana (bloated hog), TextMate (too basic) and nothing beats WebStorm. It has more than what you would expect from an IDE plus Ext JS intellisense, cross platform compatibility (Windows, Mac, Linux), and TFS support. You should give it a shot.

Once you have created a fresh workspace, add an “index.html” file with the following code.

~/index.html:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Sencha Touch MVC App</title >
    <link rel="stylesheet" href="lib/sencha/sencha-touch-2.0.0-pr3/resources/css/sencha-touch.css" type="text/css">
    <link rel="stylesheet" href="css/styles.css" type="text/css">
    <script type="text/javascript" src="lib/sencha/sencha-touch-2.0.0-pr3/sencha-touch-all-debug-w-comments.js"></script>
 
    <!-- Application JS -->
    <script type="text/javascript" src="app.js"></script>
 
    <!-- Options -->
    <link rel="apple-touch-startup-image" media="screen and (resolution: 326dpi)" href="img/startup_640.png" />
    <link rel="apple-touch-startup-image" media="screen and (resolution: 163dpi)" href="img/startup.png" />
    <link rel="apple-touch-icon-precomposed" media="screen and (max-resolution: 325dpi)" href="img/icon_57.png" />
    <link rel="apple-touch-icon-precomposed" media="screen and (min-resolution: 326dpi)" href="img/icon_114.png" />
</head>
<body>
</body>
</html>

Next add “app.js” to the root as well with the below code. This is the entry point for your Javascript application. If you set “autoCreateViewport” to true, your application will automatically look for “~/app/view/Viewport.js” as your first view. This will be the container used to serve your other views.

~/app.js:

//INITIALIZE ASYNCHRONOUS LOADER
Ext.Loader.setConfig({
    enabled: true,
    paths: {
        ' MyApp': 'app'
    }
    //, disableCaching: false //FOR DEBUGGING
});
 
Ext.application({
    name: ' MyApp',
    requires:[
        ' MyApp.model.User',
        ' MyApp.store.Users'
    ],
    controllers: [
        'User'
    ],
    models: [
        'User'
    ],
    stores: [
        'Users'
    ],
    autoCreateViewport: true
});


~/app/view/Viewport.js:

Ext.define('MyApp.view.Viewport', {
    extend: 'Ext.Panel',
 
    config: {
        fullscreen: true,
        layout: 'card',
        items: [
            {
                xtype: 'userlist'
            },
            {
                xtype: 'userdetail'
            }
        ]
    },
 
    initialize: function() {
        this.setActiveItem(0);
    }
});

For the rest of the application, create your structure like below. You can add your own flavor if you like. For example, I may merge the “css” and “img” folders into a “~/theme” folder in the future:

MVC

Notice the MVC structure and how this relates to the “app.js” file. You are registering your models, views, and controllers in there using dot notation to represent the file location. So “MyApp.controller.User” means “~/app/controller/User.js”. Since you enabled the “Ext.Loader” in “app.js”, it is asynchronously loading these files at runtime. Very cool indeed! Also worth mentioning is you can use the “requires” property in any Javascript class to initialize other classes. The same dot notation is used to locate these class files. For naming convention, folders will be lowercase and files will be uppercase (except for “index.html” and “app.js”).

Models

As with any layered application, usually the best place to start is your models or entities. Think about what objects will be used for your application and create a model accordingly (yes, we are still talking about Javascript!). Check out the code below. You are inheriting from “Ext.data.Model” and defining the property names of the object.

~/app/model/User.js

Ext.define('MyApp.model.User', {
    extend: 'Ext.data.Model',
     
    fields: [
        'ID',
        'ModifiedOn',
        'Bio',
        'Company',
        'FirstName',
        'ImageURL',
        'LastName',
        'Title',
        'Twitter',
        'WebSite'
    ]
});

Stores

The store descends from the “Ext.data.Store” class and uses your models to populate your data. This data can come from local, session, memory, AJAX, JSONP, or Direct. What interests me most here is JSONP, which allows you to make REST calls to remote servers all from the client-side. This is what decouples your client-side application from the data storage. Now you can let the data storage be served from a remote cloud or a 3rd party service such as Twitter, Flickr, Amazon, or Sencha.io. The “store” is the closest thing to server-side you may have to touch, but hopefully you have a database admin or REST services for your storage already so you can focus on pure luscious code! Below is the “store” class used for mediating data between your models and storage.

~/app/store/Users.js

Ext.define('MyApp.store.Users', {
    extend: 'Ext.data.Store',
 
    model: 'MyApp.model.User',
    autoLoad: true,
    proxy: {
        type: 'jsonp',
        extraParams: {
            'Status': 'Active'
        },
        callbackKey: 'callback',
        reader: {
            type: 'json',
            root: 'Users'
        }
    }
});

Views

You will be building the interface procedurally in this area. This means putting together available components like Lego blocks. You will be initializing the component properties within the “config” property and adding more components within the “items” property. Refer to the API documentation to see what components and properties are available for you to use. You can even create your own components which can be placed in the “~/app/ui” folder for reusability. By the way, set up an “alias” for your views using the special “widget.” format so you can use it as an “xtype” in other views.

~/app/view/user/List.js

Ext.define('MyApp.view.user.List', {
    extend: 'Ext.Panel',
    alias: 'widget.userlist',
 
    config: {
        layout: 'fit',
        items: [
            {
                xtype: 'toolbar',
                docked: 'top',
                title: 'My User App'
            },
            {
                xtype: 'list',
                itemId: 'lstUser',
                allowDeselect: false,
                store: 'Users',
                itemTpl: [
                    '<div class="user-item">',
                    '<h2>{FirstName} {LastName}</h2>',
                    '<small>{Bio}</small>',
                    '</div>'
                ]
            },
            {
                xtype: 'toolbar',
                docked: 'bottom',
                layout: {
                    pack: 'center'
                },
                items: [
                    {
                        text: 'Settings',
                        itemId: 'btnSettings'
                    }
                ]
            }
        ]
    }
});

Controllers

Notice that your views are just clean building blocks without any event handling. That is where your controllers come in. In the “init” event of your controller, you can subscribe to events of your view components. What is nice here is that you can reference components using CSS-selectors like jQuery using Sencha’s “ComponentQuery”. In the events, such as an item tap, you can pull the record clicked, process the data, and call a controller action to render a view. Sencha is working on more MVC features, so for now “rendering a view” means to activate a card in a “Panel” or pushing a view into a “NavigationView”.

~/app/controller/User.js

Ext.define('MyApp.controller.User', {
    extend: 'Ext.app.Controller',
 
    //REGISTER CLASSES
    requires:[
        'MyApp.ui.LoginForm'
    ],
 
    //REGISTER MVC
    models: [
        'User'
    ],
    stores: [
        'Users'
    ],
    views: [
        'user.List',
        'user.Detail'
    ],
 
    init: function() {
        this.control({
            //SUBSCRIBE TO EVENTS
            '.userlist #lstUser': {
                itemtap: this.onUserItemTap
            },
            '.userlist #btnSettings': {
                tap: this.onSettingsTap
            }
        });
    },
 
    //ACTIONS
    detail: function(options) {
        //UPDATE STORE WITH USER INFO
        var user = Ext.ComponentQuery.query('.userdetail #lstDetail')[0];
        user.getStore().getProxy().extraParams.UserID = options.Id;
        user.getStore().read();
 
        //SELECT USER INFO VIEW
        Ext.Viewport.getActiveItem().setActiveItem(1);
    },
 
    //EVENTS
    onUserItemTap: function (dataView, index) {
        var item = dataView.getStore().getAt(index);
        this.detail(item.data);
    },
 
    onSettingsTap: function() {
        var popup = Ext.create('MyApp.ui.LoginForm');
        Ext.Viewport.add(popup);
    }
});

Conclusion

We have only scratched the surface of what you can do with Sencha Touch. Hopefully this gives you an essential understanding of creating a base architecture for your MVC client-side application. This same concept can be used for regular websites using Ext JS, which is a subset of Sencha Touch. This means you can build client-side applications for desktop or mobile websites while sharing the same layers of code. Javascript is all grown up now!

References

HAPPY CODING!!

The following two tabs change content below.

Basem Emara

Software Architect at Falafel Software
Basem has over 10 years experience as a consultant and developer for dozens of projects for educational, advocacy, non-profit, small business, enterprise, and governmental organizations. As an innovator who recognizes and leverages the power of the web, he has been able to help real estate agencies, schools, hospitals, wholesale distributors, shipping carriers, and other businesses pioneer their industries utilizing the best that today’s technology has to offer from a more creative point of view. Simple yet elegant is the key to his approach, as well as the belief that continual process improvement is always possible.