PhoneGap and Sencha Cmd debugging techniques

If your wrapping your Sencha Touch application in PhoneGap/Cordova using Sencha Cmd to automate the tasks here are some tips for when the process does not go smoothly.

First off since your starting with an existing Sencha Touch application you initialize the project for PhoneGap with this Sencha Cmd syntax:
sencha phonegap init App-Id App-Name
App-Id is usually in reverse domain notation (com.company.AppName) but this is also used as the package in the resulting Java (at least for Android) source file.  Be sure you get it right or if you want to change it I suggest doing a sencha phonegap remove and starting over as the builds tend to fail silently in cases like this.

Also note that App-Name should be the same as the value in the “name” property in the Sencha Touch app.json config file.

 

If all you get is a white screen in the emulator there is probably a JavaScript exception or bad asset reference happening before PhoneGap/Cordova initializes.  Remember this is just an html5 app so try loading it directly in your browser (file://) and then refer to the browsers developer tools console for hints.

In Chrome F12 followed by Ctrl-O then navigate to your Sencha project directory and select the index.html in the phonegap/www directory.

 

If your using Adobe’s phonegap build service they do have built in debugging but if your app is not launching correctly or hangs before the weirne debugger sees your target try some of these techniques:

  • Download the .apk file from build.phonegap.com and load it into a running Android emulator with: adb install path/to/app.apk
  • In the case where a local build works but pushing to the phonegap build service is failing you can zip up the contents of the phonegap/platforms/android/ directory for example and manually upload it via their web interface.  This was useful for me when my local phonegap install was 3.5.0 but the Adobe service only supported up to v3.4.0

 

In the case that your sencha app build -run native command is failing silently and depending on your remote setting (phonegap.local.properties) either is not spawning the emulator or not uploading your app to the phonegap build service try executing phonegap directly:

From your Sencha project directory cd phonegap then for example phonegap build android This will give you much more verbose output which hopefully will point you to your problem.

References:

  • http://phonegap.com/blog/2013/11/20/SenchaPhoneGap/

 

Sencha Touch lists and the disclosure Icon

Sencha Touch provides a few ways to do lists, the one I most often turn to is the “simple” list.  This component has an itemTpl config where you create your markup and when you attach a store to it the template gets repeated for each item producing the HTML.

For example this code:

sencha list code 1

Produces this view:

sencha list 1

Note that the framework implements the clickable disclosure icon (added automatically because of the onItemDisclosure config) as a DIV with a CSS3 :before pseudo element for the visuals.

The problem in this layout though is that it’s absolutely positioned at the top right of each list item. For this design it is supposed to be next to the address (outlined in red for debugging).

One solution is to remove it by settting the onItemDisclosure property to false and then manually add a DIV into the address block and use the existing Sencha CSS class on it like:
<div class="x-list-disclosure"></div>
But wait that didn’t work! It looks the same, what’s the deal?

At first I thought maybe I made a mistake removing the original because the disclosure arrow was in the exact same position but no it’s the new one I just manually added. After reading the 4th reference link below it becomes clear, the absolutely positioned disclosure icon moves up to the 1st relatively positioned element. In this case the list item.

As the blog post by Chris Coyier points out this may be a CSS “Ah-ha!” moment for you… “relative positioning gives you the control to absolutely position children elements inside of it”.

OK so the solution is then easy — set the address DIV to position: relative, i.e.

.x-list-item .a-openhouse-address {
    position: relative;
}

That’s it! Now the icon stays inside the address div and because we used the Sencha class it still responds to touch events as designed.

sencha-disclosure-correct

References:

  1. http://docs.sencha.com/touch/#!/guide/list
  2. http://docs.sencha.com/touch/#!/api/Ext.dataview.List
  3. http://docs.sencha.com/touch/#!/api/Ext.dataview.component.SimpleListItem
  4. http://css-tricks.com/absolute-positioning-inside-relative-positioning/

What’s the deal with Sencha Touch generated getters and setters?

When you first start using the Sencha Touch framework you will hear about config blocks and the automatically generated getters and setters for the properties you define in them. But what does that really mean?

To review: properties you define inside a config block get framework generated getters and setters. Outside the block they are just plain javascript.

Ext.define('App.view.About', {
   extend: 'Ext.Container',
   xtype: 'aboutus',

   requires: [
   ],

   myPlainProperty: null,

   config: {

      myConfigProperty: null,

      items: [
         {
             html: 'Hello World'
         }
      ]
   }

});

If your background is object oriented (OO) development then it has been ingrained into you that accessing properties directly breaks encapsulation so you will probably default to defining your custom class properties inside the config block so you can take advantage of the generated getter and setter. For example: var foo = this.getMyConfigProperty(); and this.setMyConfigProperty("foo");

OK, now the first time you need some “logic” in a property you might resort to pulling it out of the config block and write your own getter and setter functions backed with a class instance variable like myPlainProperty above.

But as you continue to work with the framework and dive into the source code (as you inevitably will either to learn or stepping into it during debugging) you will continually see applyPropertyName and updatePropertyName methods in the Sencha code.

And these are the answer to avoiding the above hand written functions, the Sencha Class system supports two functions matching the name of your property that gives you hooks to add logic into the process.
Here are the method signatures:

  • appliedValue = applyMyConfigProperty(newValue, oldValue)
  • updateMyConfigProperty(appliedValue, oldValue)


So there you have it, put your code that does validation or filtering in the apply and take actions that depend on this property in the update!

This methodology is powerful and is used extensively in the Sencha framework so you have a wealth of code to see it in use.

For more information see the docs:
v2.2.1 Class System
and slide 32, 33 from this presentation:
Sencha Class System, Jacky Nguyen

Sencha Touch list with page numbered dividers

For a recent project I needed a list that displayed how many items were in a “page” and how many items there were in total. This way as the user scrolled they could visually keep track of where they were and how far they had to go.

This is common practice in desktop web sites with the pager links and information at the top and/or bottom of the page but in mobile we generally present a vertically scrolling list to save space and because it is easier for a touch device to drag the screen up or down than to tap a next/prev link.

Here is a screen capture of the finished product:
List screen capture

Initially I expected the solution to involve inserting “pseudo” elements into the list between the pages to contain the item counts and total or to avoid the Ext.dataview.List all together and build the layout template from scratch with raw DataItem’s. I also artificially restricted myself to the useSimpleItems=true list configuration as our dataset could be quite large and the Sencha Touch documentation warns about potential performance problems.

The other problem I was struggling with even if I could insert extra items into the list was how to not break the relationship to the store the list was bound to. In addition sorting had to be considered as that would change the item indexes and their relationship to the DOM elements.

While I was digging around in the Sencha source code to solve these problems it dawned on me I could do this with grouping! So after some trial and error a customer grouper function was all that was needed.

Here is the code you would put in the store the list is bound to:

grouper: {
    groupFn: function (record) {
        var store = record.stores[0],
            pageSize = store.getPageSize(),
            cachedCount = store.getAllCount(),
            totalCount = store.getTotalCount(),
            index = store.indexOf(record) + 1,
            totalPages = Math.ceil(cachedCount / pageSize),
            pageIndex = 0,
            lower,
            upper; 

        for (pageIndex = 0; pageIndex <= totalPages; pageIndex++) {
            lower = (pageIndex * pageSize) + 1;
            upper = (pageIndex * pageSize) + pageSize;
            if (upper > cachedCount) { upper = cachedCount; }
            if (index >= lower && index <= upper) {
               return "Properties " + lower + " - " + upper + " of " + totalCount;
            }
        }
    }
}

While the code is not that elegant and there is probably a small performance impact the advantage of this simple solution is it satisfied the visual requirements and does not require a departure from the standard well known Sencha Touch list component.

Store code available from our GitHub repository.

Enhance Sencha Cmd to work from a symlink

If you tried installing the newest Sencha Cmd on Linux (3.1.2.xxx currently) but you didn’t want to add the newly created directory to your path the first thing you might have tried was to symlink to it from a common area.
This way your users don’t have to change their paths or remember where you did the install.

$ ls -l bin/sencha
lrwxrwxrwx 1 root root 30 Jun 5 16:56 bin/sencha -> ../Sencha/Cmd/3.1.2.341/sencha

But that doesn’t work because the script looks for it’s dependencies based on it’s location:

$ sencha
Sencha Cmd folder (/usr/local/bin) is missing sencha.cfg - aborting!

So you try symlinking the config file:

$ cd /usr/local/bin/
$ sudo ln -s ../Sencha/Cmd/3.1.2.341/sencha.cfg .


$ sencha
Error: Unable to access jarfile /usr/local/bin/sencha.jar

Well we can see this is going to get tedious…

The solution is simple but requires you modify the shell script wrapper that Sencha provides (sencha) to use the readlink command to de-reference back to the actual install directory.

-BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+BASEDIR="$( cd "$( dirname $( readlink -m -n "${BASH_SOURCE[0]}" ) )" && pwd )"

Cool, now it works:

$ sencha
Sencha Cmd v3.1.2.342
...

You can also see my suggestion on the Sencha forum:
http://www.sencha.com/forum/showthread.php?265378-Minor-enhancement-to-Sencha-Cmd-(symlink-support)