Sencha Inc. | HTML5 Apps

Blog

Reusing Code in Ext JS 5 and Sencha Touch Apps to Build a Sencha Space App

June 12, 2014 | Grgur Grisogono

Guest Blog Post

With Ext JS 5, reusing code between mobile (Sencha Touch) and desktop/tablet apps (Ext JS) is a dream come true. In the following exercise, we will harness the power of Sencha Cmd 5 to build a Sencha Space application that's capable of sharing code between Ext JS 5 and Sencha Touch 2.

Our goals are to:

  • Create Sencha Touch and Ext JS apps that reuse as much code as possible
  • Use packages to contain shared features
  • Learn how to tackle incompatibilities
  • Deploy apps in a Sencha Space environment

To reach our goal, we will create an app that displays data for the most popular web browsers of 2012 in form of a grid and chart. The entire source code presented in this exercise is available for you to fork or download.

Preparation

Good preparation is the key to success. Workspace and packages are old pieces of a puzzle that provide a much clearer picture of multi-application environments now that Ext JS 5 is available.

Let's start with creating a workspace. Make sure you have Sencha Cmd 5 installed.

gmac15:modus-presos grgur$ sencha generate workspace Ext5Tablet
Sencha Cmd v5.0.0.160
...

Now, we have a workspace directory that will host our applications and the frameworks. Navigate to the workspace directory and run:

gmac15:Ext5Tablet grgur$ sencha -sdk /path/to/ext-5.0.0 generate app MyApp ./ExtSpace

We will refer to our app as MyApp. This Ext JS 5 application will serve our tablet users. To suppport our phone users, we will create a separate Sencha Touch application:

gmac15:Ext5Tablet grgur$ sencha -sdk /path/to/touch-2.3.1-complete generate app MyApp ./TouchSpace

Finally, it's time to create our package that contains shared code. We will code a chart and a grid/list component in it, so let's call the package GridChart. From your workspace directory execute:

sencha generate package GridChart

This is what our file structure looks like now:

ExtSpace and TouchSpace are application folders, packages/GridChart contains shared components, while ext and touch reference framework SDKs. In the following sections, we will take a deeper dive into each of these pieces.

Shared Chart Components

Ext JS 5 includes the amazing new charts from Sencha Touch 2. Best of all, their configuration pattern is very similar, so we can easily reuse them in our apps.

The Ext.chart.Chart widget is capable of intelligently instantiating the needed chart — and it does that for both frameworks.

 
Ext.define('GC.Chart', {
    extend : 'Ext.chart.Chart',
    xtype  : 'gcchart',
 
    config : {
        animate      : true,
        background   : 'white',
        insetPadding : 40,
        interactions : 'itemhighlight',
        legend       : {
            docked : 'bottom'
        },
 
        colors: [...],
 
        series: [...],
        axes  : [...]
    }
});
 

Full source code is available here.

Note: Charting in Sencha Touch is part of the Sencha Complete bundle.

Two patterns are visible in this example. First, the code will be shared — meaning the entire class definition is compatible with both Sencha Touch 2 and Ext JS 5.

Second, Ext JS 5 understands properties from config object and maps them to those outside of the block. That's why this chart configuration looks like Sencha Touch but works just fine in Ext JS.

Now that we have a universal chart widget, we can take a look at how we can create shared code that is slightly less common for the two frameworks.

Grid vs. List

We want to show plot data in a grid-like view. The Ext JS grid widget will perform the task for tablets and the Sencha Touch List will display data for phones. Nonetheless, the code for both will exist in the same package.

The beauty of this approach is in a single line that belongs to the 'package.json' file of our GridChart package. From the workspace directory, edit the ~/packages/GridChart/package.json file and add the following line:

 
"classpath": "${package.dir}/src,${package.dir}/src-${framework.name}"
 

The part that resembles 'src-${framework.name}' will allow us to have two folders, each containing source code just for its respective framework. Those folders will be 'src-touch' (List) and 'src-ext' (Grid).

Both widgets will be very simple. For tablets, our grid setup is as follows:

 
Ext.define('GC.Grid', {
    extend : 'Ext.grid.Panel',
    xtype  : 'gcgrid',
 
    title       : 'Browsers',
    split       : true,
    collapsible : true,
    columns     : [
        {
            text      : 'Month',
            flex      : 1,
            dataIndex : 'month'
        },
        {
            header    : 'IE',
            xtype     : 'numbercolumn',
            width     : 70,
            dataIndex : 'data1',
            format    : '0,0%'
        },
        {...},
        {...},
        {...}
    ]
});
 

Full source code is available here.

On the other hand, the List is all about its template:

 
Ext.define('GC.Grid', {
    extend : 'Ext.List',
    xtype  : 'gcgrid',
 
    config : {
        cls     : 'browserlist',
        itemTpl : [
            '<h1>{month}</h1>',
            '<div class="browsers">',
                '<b>IE</b> {data1} ',
                '<b>FF</b> {data2} ',
                '<b>Chrome</b> {data3} ',
                '<b>Safari</b> {data4} ',
             '</div>'
        ].join('')
    }
});
 

Full source code is available here.

We can wrap it up with our simple approach to displaying data. The following section is all about plugging this code into the platform-specific code of each application.

Tablet Layout

Shared code makes up a large part of our apps, containing most of the business logic. Plugging it in is as easy as appending a single line into the '~/ExtSpace/app.json' file of our Ext JS application.

 
     "requires": [
        "sencha-charts",
        "GridChart"
    ],
 

Full source code is available here.

Ext JS 5 comes with a modular approach to dependencies. You’ll also want to use the 'sencha-charts' package. Sencha Cmd 5 knows how to find each of these and will make the classes available to the application.

Note: if you browse to the Ext JS 5 SDK directory, the packages folder will have ext-charts and sencha-charts listed. Ext-charts refers to the older charting package, whereas sencha-charts is the all new and improved code.

Before proceeding to editing the Main view, you will learn a new trick with Cmd 5 — app watch. Make sure your terminal is in the directory of your application (e.g. ~/ExtSpace) and run:

gmac15:ExtSpace grgur$ sencha app watch
Sencha Cmd v5.0.0.160
[INF] Loading app json manifest...
...
create MyApp-all.css
[INF] Mapping http://localhost:1841/ to /Users/grgur/Projects/modus/modus-presos/Ext5Tablet...
[INF] ------------------------------------------------------------------
[INF] Starting web server at : http://localhost:1841
[INF] ------------------------------------------------------------------
[INF] Waiting for changes...

Many of the seasoned developers are already familiar with this command, which sits in the background and watches for changes in SASS and JavaScript files. Any changes will automatically do the minimum amount of work to update the app — whether it's CSS compilation or class metadata refresh.

Additionally, the command will now instantiate the built-in Jetty web server to host your files hassle free.

With everything ready, we can create the main view:

 
Ext.define('MyApp.view.main.Main', {
    extend : 'Ext.container.Container',
    xtype : 'app-main',
 
    requires: [
        'GC.Chart',						//#1
        'GC.Grid',						//#1
        'Ext.data.JsonStore',
        'Ext.chart.*'
    ],
 
    layout : {							//#3
        type : 'border'
    },
 
    config : {							//#2
        grid : 'west',
 
        chart : 'center',
 
        store : [						
            { month : 'Jan', data1 : 20, data2 : 37, data3 : 35, data4 : 4 },
            //...
            { month : 'Dec', data1 : 15, data2 : 31, data3 : 47, data4 : 4 }
        ]
    },
 
    /**
     * Instantiate a store based on data&#44 storeId or store instance
     */
    applyStore : function (data, store) {		// #5
        return Ext.isString(data) || data.isStore ? Ext.getStore(data) : Ext.factory({
            fields : ['month', 'data1', 'data2', 'data3', 'data4' ],
            data   : data
        }, 'Ext.data.JsonStore', store);
    },
 
    /**
     * Instantiate shared chart
     * @param region
     * @param chart
     * @returns {GC.Chart} Chart
     */
    applyChart : function (region, chart) {	// #5
        return Ext.factory({
            region : region,
            width  : 400,
            store  : this.getStore()			// #4
        }, 'GC.Chart', chart);
    },
 
    /**
     * Instantiate grid
     */
    applyGrid : function (region, grid) {		// #5
        return Ext.factory({
            region : region,
            width  : 400,
            store  : this.getStore(),			// #4
            title  : 'Browsers in 2012'
        }, 'GC.Grid', grid);
    },
 
    /**
     * Add items to the view
     */
    initComponent : function () {
        var me = this;
 
        me.items = [
            me.getChart(),
            me.getGrid()
        ];
 
        me.callParent();
    }
});
 

Full source code is available here.

Just by reviewing the code above you will notice a few gotchas:

  1. Previously we included the GridChart package in our app, so Sencha Cmd could correctly map its location. In order to use any of the classes from that package, we must still require them as per the Sencha convention.
  2. Unlike Sencha Touch 2, Ext JS 4 configuration options are not members of the config object. For cross-framework compliance, Ext JS 5 is now able to recognize most parameters from config objects that should otherwise belong to the constructor. In this example, we are re-using the store property and adding two new: grid and chart. Both grid and chart are strings representing a location in the border layout.
  3. Layout is an exception for the point above. Due to differences in Ext.Component lifecycle between Sencha Touch 2 and Ext JS 5, layout configuration has to be outside of the config block.
  4. Both grid and chart components reuse the same local data store.
  5. The Sencha Touch modular component instantiation, which uses factory method and appliers, is now Ext JS compliant. See applyStore, applyChart, and applyGrid methods.

Tip: When creating shared components in packages, we can override 'constructor':

 
constructor: function (config) {
    config.layout = 'border';
    this.callParent([config]);
}
 

Most of the hard work is now done. If you run the Ext JS 5 application in your browser, you will see something like this:

Let's replicate this view in Sencha Touch.

Phone Layout

Before adding any view code, we will revisit this app's app.json file in ~/TouchSpace/app.json:

 
"requires": [
    "GridChart"
],
 

Full source code is available here.

This time there is no need to reference sencha-charts because charts are not in the form of a package in Sencha Touch 2.3.

You can run sencha app watch for this app as well to help streamline updates.

Now, let’s edit the Main view that was automatically generated for us and update it to match something like this:

 
Ext.define('MyApp.view.Main', {
    extend   : 'Ext.Container',
    xtype    : 'main',
    requires : [
        'GC.Chart',
        'GC.Grid',
        'Ext.data.JsonStore',
        'Ext.chart.*'
    ],
    config   : {
        layout: {
            type: 'hbox',
            align: 'stretch'
        },
 
        grid  : 1,
        chart : 2,
        store : [...]
    },
 
    applyStore : function (data, store) {...},
 
    applyChart : function (flex, chart) {
        return Ext.factory({
            flex  : flex,
            store : this.getStore()
        }, 'GC.Chart', chart);
    },
 
    applyGrid : function (flex, grid) {...},
 
    /**
     * Add items to the view
     */
    initialize : function () {
        var me = this,
            grid = me.getGrid(),
            chart = me.getChart();
 
        me.add([
            grid,
            chart
        ]);
 
        me.callParent();
    }
});
 

Full source code is available here.

Instead of border layout, we used horizontal box. Everything else looks similar to the code we used in the Ext JS variation of the app.

The main difference is in the initialize and initComponent blocks where Ext JS needs to override this.items property, and Sencha Touch makes use of the add method. This is simply because Sencha Touch creates DOM elements during instantiation, while Ext JS does so during render phase, which happens just before inserting the widget into the DOM.

With sharing so much of the code from the Ext JS application, we developed a similar user interface in just a few minutes. If you haven’t run it in a browser yet, this is what you will see:

Deploying in Sencha Space

Sencha Space is a secure, cross-platform environment for mobile web and HTML5 apps that helps organizations deliver enterprise applications to end users. If you are new to Sencha Space, you can watch the webinar Deep Exploration of Sencha Space.

Our new application will benefit from being part of this ecosystem and use its API to communicate with other apps.

Provided that you already have an account, log in to your Sencha Space Management Console. From here, add the apps and make sure they point to your local web server. No need to make the app publicly available quite yet, which will help with the debugging process.

Make sure you set up invoke intent strings for both applications (see figure below). This demo uses *bpmobile* for phone (Sencha Touch) and *bprich* for tablet application (Ext JS).

We will revisit app.json files of both applications just to add paths to Sencha Space APIs. If you wish to debug, you can also add a path to your local Weinre instance.

 
 "js": [
    {
        "path": "http://space.sencha.io/space.js",
        "remote": true
    },
//    ...
]
 

Full source code is available for the Ext JS 5 app and the Sencha Touch 2 app.

Finally, one last gem. We want our apps to detect form factor and prompt the user to switch to the other app for a better experience. For example, phone users accessing the Ext JS app should be told that a more applicable solution exists for their device.

Your Ext JS app's ~/ExtSpace/app/Application.js file will have the following addition:

 
launch : function () {
    var me = this;
 
    Ext.onSpaceReady(function () {
        var width = Ext.getBody().getWidth();
        if (width < 1000) {
            me.transferToMobile();
        }
    });
},
 
transferToMobile : function () {
    Ext.Msg.confirm('Phone detected', 'Do you want to see the phone site instead?', function (resp) {
        if (resp === 'yes') {
            Ext.space.Invoke.get('bpmobile').then(function (connection) {
                connection.send({}, true);
            }, console.warn.bind(console));
        }
    });
}
 

Full source code is available here.

Ext.onSpaceReady fires when the Sencha Space ecosystem is ready to use, which happens milliseconds after the document becomes available. Our handler will prompt the confirmation message first, then use Invoke API to bring the Touch app to the foreground. Notice the bpmobile invoke string that identifies which app to communicate with.

There is no better reward than to sit back and enjoy the results of our coding session.

Ext 5 blog post from Modus Create on Vimeo.

Source

Full working source for this application is available here. All you need to do is load it in a browser or your Sencha Space instance.

Conclusion

Ext JS 5 offers many new features for enterprise apps, one of which is support for integration and code sharing with Sencha Touch. However, the integration capabilities extend much further with Sencha Cmd 5 and Sencha Space, delivering the most feature rich frameworks to satisfy any enterprise environment. In this example, we also demonstrated the power of the brand new Sencha Charts feature inherited from Sencha Touch 2. Ext JS 5 delivers an impeccable, smooth user experience on both desktop and tablet devices.

There are 5 responses. Add yours.

Blake

3 months ago

There’s no good reason to split your efforts between Touch and Ext JS. Just build your app in Touch, add a profile for Tablet / Desktop, deploy with node-webkit and a scroll fix (https://gist.github.com/nktpro/3044584) and be done with it! When IE10 support comes, allow access through the browser. Just my 2 cents.

Dawesi

2 months ago

Awesome post… loved it.

———

@blake The only good reason to do what you suggested is if you’re writing a small, or simple app that isn’t targeted at enterprise at all. (aka end user single use app)

I definitely wouldn’t recommend starting with touch as the core on any enterprise project with a lot of data that needs to be cross referenced. That would be a disaster.

Just on accessibility you’re dead in the water starting with Sencha Touch, a requirement of any govt or large enterprise app by law, countries are now moving to WCAG 2 AA, touch doesn’t even have section 508…

...Lists aren’t anywhere as useful as grids (the killer feature of ExtJS) and nested lists not anywhere as useful as trees (as another example)... and I’m just getting started and the discussion is over. ExtJS has tonnes more functionality and use than touch does.

Mobile is a complimentary platform for most enterprises and it will be for a while yet. Mobile is a great consumer app platform, but is still to be realised in enterprise. Enterprise is mobile supplementary, not mobile first. (for now)

My 2c

Blake

2 months ago

@dawesi Touch really is a good platform for large apps. I’m not sure where you’ve got the idea that its data package or api is somehow only suitable for small, simple apps. That hasn’t been my experience at all. The ExtJS4/Touch2 data packages have for the most part the same features, same pain points, and are being merged.

Touch has an infinite grid with draggable, sortable columns, so I think it’s better to compare that, instead of its list, to the grid. Some of the components have no exact equivalent, like the tree, but those are largely outnumbered by the duplicate components. Design is all about honing down products, simplifying, removing the extra crap, so I’d be willing to bet most companies would even benefit from fewer features.

Overall, this divide is largely historical and, I believe, will be wiped away once IE8+9 are no more. Most businesses have to develop a web-based desktop app anyway; So it is / will be in Sencha’s best interest to sell its mobile product based on its desktop functionality, save cannibalization.

But, if you use node-webkit, you no longer have to deal with older browsers so it allows you to leap-frog everyone else. Why wait to experience that zen that is write once, run everywhere? smile

Tom

2 months ago

Thanks for this post! We translated it into Japanese here: http://www.xenophy.com/sencha-blog/11270

and the video with Japanese subtitles is here: http://vimeo.com/98302075

Sonia

2 months ago

Thanks a lot for this !
Just a question : there were an available connector Touch for Azure Mobile Service.
Does Ext JS 5 can use this same connector ? We didn’t want to manage any backend for the datas !
And we are looking forward to use Ext JS 5 with all the new functionnalities !

Comments are Gravatar enabled. Your email address will not be shown.

Commenting is not available in this channel entry.