Sencha Touch for the PHP Developer

The beginning of the year saw me working on the latest version of the Herald Scotland app (both android and iOS versions were released at the beginning of May) using Sencha Touch and PhoneGap. App development is not one of my primary skills and the learning curve from my comfort zone of PHP to Sencha Touch has been a steep one and one which I’m still far from conquering, however I thought it might be useful to share some of what i have learned and hopefully steers others away from some of the issues I encountered.

So you can build a website?

If you only take away one thing from this article then it should be to forget everything you know about web development and start again… Sencha Touch is an entirely different beast to what you are used to.

With Sencha Touch the page is built from a collection of javascript files which create the output of the page so there is no nice easy HTML file to work with.  You should hopefully already be familiar with a DOM inspector and this will be your best friend when trying to figure out how all the pieces related.

If you consider a standard page within a website you will notice each page has a similar flow i.e. header, main content, footer

traditional-page.jpg

In a Sencha Touch application the flow is a bit different (header, a container for the main content, footer).

sencha-page.jpg

The main difference is to do with the container to hold the main content. To view several pages on a website each page is loaded in turn and the header, main content and footer is generated and added to your page. In your Sencha Touch app only 1 page is loaded, 1 header and footer is generated and the main content for several pages is added or removed from the main content container as and when required.

If you were to follow the process of a traditional webpage in Sencha Touch you would have several headers and footers and if you had any listeners in them then you would also be listening to several instances of the same listener.

Understand what markup is necessary

Using an Ext.List is a perfect way to display a list of content i.e. a list of news articles, contacts, sports teams etc, but here you must again refrain from trying to do exactly as you would do when creating a list on a traditional webpage i.e. On a traditional webpage you may have something like the following for each article

<div class="item">
    <h2><a href="...">[First Name] [Last Name]</a></h2>
</div>

But within the itemTpl of an Ext.List you have to remember the whole item is treated as the equivalent of an A link, more accurately, clicking the item triggers an event listener which you can attach logic to.  As a result, to use the example above in an Ext.List itemTpl you should use the above without any A tags and make use of the itemtap event to determine what to do.

Although the example below is a bit longer than the HTML version above, the key point is the itemTpl line as you will notice there is no <a href> but we are able to determine which item is clicked by the itemtap listener.

Ext.application({
   launch: function() {
       Ext.define('Contact', {
           extend: 'Ext.data.Model',
           config: {
               fields: ['firstName', 'lastName']
           }
       });

       var store = Ext.create('Ext.data.Store', {
           model: 'Contact',
           sorters: 'lastName',
           grouper: {
               groupFn: function(record) {
                   return record.get('lastName')[0];
               }
           },
           data: [{
               firstName: 'Tommy',
               lastName: 'Maintz'
           }, {
               firstName: 'Rob',
               lastName: 'Dougan'
           }, {
               firstName: 'Ed',
               lastName: 'Spencer'
           }, {
               firstName: 'Jamie',
               lastName: 'Avins'
           }, {
               firstName: 'Aaron',
               lastName: 'Conran'
           }, {
               firstName: 'Dave',
               lastName: 'Kaneda'
           }, {
               firstName: 'Jacky',
               lastName: 'Nguyen'
           }, {
               firstName: 'Abraham',
               lastName: 'Elias'
           }, {
               firstName: 'Jay',
               lastName: 'Robinson'
           }, {
               firstName: 'Nigel',
               lastName: 'White'
           }, {
               firstName: 'Don',
               lastName: 'Griffin'
           }, {
               firstName: 'Nico',
               lastName: 'Ferrero'
           }, {
               firstName: 'Jason',
               lastName: 'Johnston'
           }]
       });

       Ext.create('Ext.List', {
           fullscreen: true,
           itemTpl: '<div class="item"><h2>{firstName} {lastName}</h2></div>',
           store: store,
           grouped: true,
           listeners: {
               itemtap: function(view, index, item, e){
                   Ext.Msg.alert(e.data.firstName);
               }
           }
       });
   }
});

Source available at https://fiddle.sencha.com/?fiddle=6he#fiddle/6he

Example adapted from https://try.sencha.com/touch/2.0.1/docs/Ext.dataview.List.2/viewer.html”ltr”>Sencha Touch has a good set of tools available for helping you develop your app and you should get familiar with what tools are available and what they can do for you.  Sencha CMD provides a command line tool for generating your app and preparing it for release.  In the early stages of building my first version of the app I wasn’t aware of the Sencha CMD command line tools and later when I tried to use Sencha CMD with my app it was a nightmare and I basically had to create a new app using Sencha CMD and then import my code into the new app.  Test early and test often and it’ll save headaches further down the line.

It’s also worth taking a little time to get familiar with the tools can do.  I lost a day or so when trying to package up the app for release as I was using

sencha app build production

instead of

sencha app build package

(Think website application vs Android / iOS app)

Sencha Touch also has a really good documentation along with examples and an active forum and although it can take a bit of time to work out some of the solutions that have been posted it does help to shed light onto lots of issues you may encounter.

Program Flow & Callbacks, Callbacks, Callbacks

In PHP we are used to a sequential program flow… the next code statement is run only when the current one is complete and if we call a method we get the response of that method before we continue on with the next code statement.  

Sencha Touch follows an asynchronous program flow which can cause some strange issues if you don’t take this into account.  For example, when you call a method within your code, the logic within that method is executed but the application also continues onto the next line of code after the method call without waiting for a response to be returned.  This can cause race conditions as if a subsequent action is dependant on the response from that method then this may not be set correctly, especially if the function takes time to run.  This can be demonstrated in the following (over-simplified) example:-

Ext.application({
    name: 'Program Flow Example Without Callback',

    launch: function() {
        this.firstLine();
        this.secondLine();
    },

    firstLine: function(callback) {
        // Increment the counter slowly
        var counter = 0;
        for(i=0;i<=10;i++) {
            Ext.Function.defer(function() {
            	counter++;
                if(counter == 10) {
                    document.write('This is the first line<br>');
                }
            }, 100);
        }

    },
    secondLine: function() {
        document.write('This is the second line<br>');
    }
});

Source available at https://fiddle.sencha.com/?fiddle=6cp#fiddle/6cp

If this was run via PHP we would expect the output to be

This is the first line
This is the second line

However, as Sencha carries on executing the next line of code as soon as it calls your method call, we get

This is the second line
This is the first line

This drove me crazy for a while as in my case my application beat the race condition on one run but did not necessary beat the race condition on the next run, therefore giving me an very intermittent issue until I understood why it was occuring.  The resolution to this is to use a callback and depending on your application the callbacks can get nested quite deeply.  Taking the above example again, if we refactored it to use callbacks it might look like

Ext.application({
    name: 'Program Flow Example With Callback',

    launch: function() {
        var me = this;
        
        this.firstLine(function() {
            me.secondLine();
        });
    },

    firstLine: function(callback) {
	// Increment the counter slowly
        var counter = 0;
        for(i=0;i<=10;i++) {
            Ext.Function.defer(function() {
            	counter++;
                if(counter == 10) {
                    document.write('This is the first line<br>');

                    // Now run the second line function 
                    // (via the callback passed in)
                    callback();
                }
            }, 100);
        }

    },
    secondLine: function() {
        document.write('This is the second line<br>');
    }
});

Source available at https://fiddle.sencha.com/?fiddle=6cq#fiddle/6cq

This time, we pass a callback to firstLine which is use to execute secondLine at the appropriate point and now we get the out put in the correct sequence

This is the first line
This is the second line

Naming Convention

This one is more of a personal preference which spills over from my PHP development rather than a requirement of Sencha Touch.  Within the PHP framework we use we add a suffix of Controller, View, Model etc as required to our files which allows me to quickly identify which files are for what part of the application.

When starting out with Sencha Touch I quickly found it confusing trying to figure out which tab in my IDE was for which file when I had a controller, view & model all called Article.js.  This got even more confusing when I had separate tablet & mobile files but by simply adding Controller, View & Model to the end of the filename as appropriate all the confusion was removed.

Leave a Reply

Your email address will not be published. Required fields are marked *