Upgrading to Sencha Touch 2 PR2

Sencha Touch 2 brings a wide range of major enhancements and improvements to make building fast and powerful mobile web applications easier than ever. These improvements include vastly better Android performance, a new layout engine, the Sencha class system, a far more consistent API, and native packaging.

But what does this mean for you and the lines of code in your application?

When upgrading a dependency of any application, such as a library or framework, developers of the application itself normally need to think about a number of things:

Changes to best practices - are there now better and more future-friendly ways to do things?

Changes to underlying APIs used by the application - have parts of the API changed name? become deprecated? changed their return types?

New possibilities - is new functionality now available for your application to make use of?

Other interoperability issues & testing - did you make implicit assumptions about how the previous version worked that are no longer true? Do your application's tests still pass?

In this article, we'll take a very simple Sencha Touch application - the City Bars guide - and think about each of these areas, showing how to make it run on the PR2 release of the Sencha Touch 2 framework. This release is still evolving, and some areas of the API are very likely to change as the framework approaches general release.

There are also areas of the Sencha Touch 1.1 API which have yet to be migrated to Sencha Touch 2 at all - notably routes and profiles, for example - so some applications will not yet be upgradable as-is. Nevertheless, much of the process we will go through here will apply to your own upgrade efforts, regardless of the version you end up using.

The City Bars application itself uses a Yelp API to pull data about city businesses from a JSON API. The user interface comprises a card-layout, containing a list of twenty matching businesses, and a disclosure-style transition to a detail page about a selected business. (In the Sencha Touch 1.1 version of the application, the detail page also included a map of the business' locations. For this article, we've removed that second tab, due to known preview release issues in animation and map-in-tab layout.)

Getting started

The developer preview release of Sencha Touch 2 does not guarantee backwards compatibility with Sencha Touch 1.1, but our first task should be to at least try. After downloading Sencha Touch 2 PR2, and unzipping the SDK, we can drop it into the app, renaming it lib/touch2 alongside the existing lib/touch folder.

As usual, the references to the SDK files are all in the index.html file. We can easily rename the path in the JavaScript and stylesheet links to point to the new SDK:

Note that we have used sencha-touch-all-debug.js, not sencha-touch.js. In Sencha Touch 2, the -all suffix is the complete library, containing all of the framework's components and libraries. One of the framework's biggest improvements in v2 is its class loader, which allows the application to download script files on demand - and this technique would allow us to load the smaller sencha-touch.js file to bootstrap the application, and then cause other components to be pulled from the server when needed.

But to get this project going quickly, we'll simply use the master framework file. And at this point it's also valuable to use the -debug version, which will enable warnings about deprecated APIs and so forth. Running the application in a desktop browser allows us to see console logging and script errors, and it's a good idea to do this before sending the upgraded application to real devices, which generally lack good diagnostic tools. Here, we're using Chrome, but Safari will also suffice.

When we run this new application, we see a blank screen, and nothing on the console. Apparently, the script 'runs' successfully, but nothing displays! Has the application been started? Never fear - the Sencha Touch 2 'Getting Started' guide illustrates that there is a new best practice for starting an application. Rather than simply instantiating an application class, we're encouraged to use the Ext.application() method (thankfully with the same configuration object). Note the capitalization.

So, to get things started, we simply change:

var cb =new Ext.Application({...

to:

Ext.application({...

This method not only instantiates the application, but also binds its launch event to the page's load event. (This was the missing step that caused our page to be blank.) It's also worth pointing out that the method does not return a reference to the new application (which we'd previously used as a global namespace of sorts), so we should take care to manually declare a namespace at the start of the script which we can then use to reference the app, and into which we can put useful references.

var cb;
Ext.application({
launch:function(){
cb =this;

(It's a known issue that the configuration's name property does not automatically create the namespace on its own.)

With that, our application now at least starts up, as witnessed by the deprecation warnings that appear in the console:

And the app does now appear - or at least, the docked toolbar does:

Why does nothing happen once the user interface gets laid out? Well, if you look at the original code, you'll see our application is based on a series of asynchronous calls to determine the city name and then to look up business information. (As it happens, we've hard-coded the city name, but this pattern better enables us to potentially wire up the app for geolocation of the user, and then use a dynamic city name in the store proxy's URL). This callback sequence is tied to the main panel's afterrender event - in other words, when we know the application's user-interface is ready so that we can display a loading spinner.

Since there are no errors being thrown, our first consideration should be for whether we are attaching our logic to the right event any more. We take a quick look at the Ext.Panel's documentation and notice that, indeed, afterrender is no longer available as an event - hence the reason our code wasn't executed. Looking through the list, we can see that event is not relevant any more. Because of changes to the layout engine, we can be sure that the component is available as soon as it's been instantiated.

So all of the code that was in the afterrender event:

listeners:{'afterrender':function(){// when the viewport loads...

...can be moved out into the main part of the launch(), just after the component's instantiated.

Success! Or at least we now have output from the console. And an error:

...which means it is now time to start looking at the API calls we are using, and ensure they are still available and appropriate in Sencha Touch 2.

API changes

Understanding changes to an API is sometimes a matter of trial and error. Sometimes the changes are obvious (such as here, where an exception is thrown), sometimes they are raised as warnings by deprecation flags, or sometimes simply learnt by reading upgrade documentation (like this article! - or this one). In this walkthrough, we just push on through the exceptions that are raised, confirming the changes we need to make by consulting the documentation.

Our first, as shown in the console above, is that the setLoading() method is not available on our root panel. Looking at the code, we can see there is an undocumented mask() alternative we could use, but preferably, we can create a panel-independent loading mask using Ext.LoadMask, and bind it to the businesses store, just after it's been registered:

new Ext.LoadMask(Ext.getBody(),{
store:'businesses',
msg:''});

Its appearance and disappearance will then be taken care of by the store's own loading events, and we can remove all explicit setLoading() calls.

Sencha Touch 2 has changed the differentiation between docked and child items of containers, and rather than using a distinct dockedItems configuration property, docked items are now listed in the main items property, and need to be given a docked property to indicates docking position. So in general, we need to change things like:

Along with this change, the getDockedItems() method has been removed, and we need to use the regular item accessors to reach the toolbar - for the purpose, in this app, of updating the title dynamically. The simplest change is to replace:

cards.listCard.getDockedItems()[0].setTitle(...);

with:

cards.listCard.items.items[0].setTitle(...);

However, this assumes that the docked items are added to the beginning of the items array. For more resilience, you may prefer to use a ComponentQuery to reference child items. This selector will return the first top-docked toolbar, for example:

cards.listCard.query('toolbar[docked="top"]')[0].setTitle(...);

And with that, we push past our final start-up exception and reach a working list view:

So far, we've only changed around 5% of our original lines of code. Not too bad.

Our next exception comes when we try to transition from the list to the detail page. It's slightly less helpful ('DOM Exception 8') but a quick stacktrace highlights that the issue lies on line 109 of our app:

On this line, we are using an update() method. We should already suspect there is something afoot here, with the many warnings above that indicated that update() has been deprecated.

Although these warnings recommend the usage of setHtml() instead, we were using the other previous meaning of the update() method, which was to apply data to a template - in this case, the data of the business' record to the template on the detail page. For this purpose, we should replace update() with a different new method, setData(). We can more or less find-and-replace here, noting that the original application also overrode the method on the tab panel to cascade updates down to each tab. We can still use that pattern, and, because docked items are now first-class members of their parent's item arrays, we can even override the method on the toolbar of the detail page to update it with the restaurant's name:

Remember, we're removing the map functionality, so we can also remove the detail card's xtype:'tabpanel', tabBar property, and the Ext.Map child tab item.

Our application is no longer throwing exceptions, so we can probably declare that we've updated the app to match the new Sencha Touch 2 API. Now we can move onto dealing with more subtle alterations, such as those as detailed in the remaining deprecation warnings - not to mention a slightly strange layout you may notice we now have in the detail page.

New Best Practices

Sometimes the warnings that Sencha Touch 2 generates are very explicit about how to upgrade API calls to make better use of the framework's updated architecture. For example:

Ext.regModel has been deprecated. Models can now be created by extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});

This warning hints at a significant change we can start to make to our code: the use of the methods Ext.define() and Ext.create() to start capitalizing on the benefits of the framework's new class loader. This article won't go into detail about the system as a whole (for that, please see Jacky's excellent article), but for now, it is sufficient to say that we should start using Ext.define() when defining components or classes (as with the models above) and Ext.create when instantiating them. This will mean that the Sencha Touch loader and build tools will be able to keep track of your class structure and instantiations when you come to use them.

So for example, as the warning recommends, extending the model class to describe businesses can use Ext.define(), and

Ext.regModel("Business",{
fields:[...

should become:

Ext.define("Business",{
extend:"Ext.data.Model",
fields:[...

We should also hunt down the use of the new keyword, and consider replacing it with Ext.create(). For example:

cb.cards=new Ext.Panel({...

should become:

cb.cards= Ext.create('Ext.Panel',{...

…and even our recently-added:

new Ext.LoadMask(Ext.getBody(),{...

should become:

Ext.create('Ext.LoadMask', Ext.getBody(),{...

At this point, we should also track down any remaining deprecation warnings relating to our application. This one in particular seems relevant:

However, you may also have noticed a selection of other deprecation warnings being emitted by the application:

At some point, you may decide that none of the remaining warnings are actually because of direct calls in your code. In our case, this is now the case, and is because parts of the framework have not yet themselves been updated to use new methods or patterns. As Sencha Touch 2 approaches a more mature release, these updates will all have been done, and such 'internal' warnings will cease to exist.

Final Tweaks

As we suspected, we should always expect the possibility of interoperability issues, especially when upgrade to the preview release of a framework. In our case, the biggest offender seems to be a problem with the layout of the detail page.

As you'll know, one of Sencha Touch 2's major improvements has been a huge performance gain in the user-interface layout engine. This has been achieved by using more of the target browsers' own CSS layout engines, where appropriate, and in particular the use of the CSS3 flex box model. This can sometimes interfere with previous assumptions we've made about layout styling, for example. And the likely cause of the issue we are seeing here.

Yet another of the advantages of using a desktop browser debugger is that we can explore the DOM of the app's user interface to figure out what has gone wrong with our particular layout, and by right-mouse clicking on any of the elements in this 'broken' page, we can explore their CSS properties to see why they are arranged horizontally instead of vertically as we wanted.

In this case, the issue is that the x-container of the detail panel has a display: -webkit-box; property but no orientation (which tends to default to horizontal). We can quickly fix this with an extra CSS rule on this panel to make it explicit:

.detail{-webkit-box-orient: vertical;}

Also we notice that the HTML formatting (such as the larger <h2> heading in the detail page) is missing. Our use of styleHtmlContent: true on the detail page is not being obeyed for some reason, so we can explicitly set it so as part of the overridden setData() method:

We should also remove the float property from the image to make sure it plays nicely with the cascading flex box:

.detail .photo{
float:none;}

Obviously developers should expect the need for these kinds of tweaks to decline dramatically as the framework approaches its mature release.

Finally, it's worth mentioning custom theming. As you hopefully know, Sencha Touch uses Sass extensively to provide a theming framework that makes it easy for you to update entire user interface appearances with minimal changes. This approach relies on a set of Sencha Touch Sass modules, and it goes without saying that these have also been updated for Sencha Touch 2.

What this means is that you will not be able to use previously-compiled CSS files with Sencha Touch 2 apps, and you will need to recompile your own Sass with the new modules from the Sencha Touch 2 SDK. You're unlikely to need to make any changes to your Sass file though, and in our case, we can simply change the Compass config.rb to point to the lib/touch2 SDK, recompile, and link to a new citybars.css file in our index.html.

Et voila:

And all with only 22 very minor changes to the application - at least, as far as my diff tool claims!

The full app is available in the citybars2 GitHub repository (although you will need to copy the Sencha Touch PR2 SDK into the app yourself to run it locally). You are strongly advised to add your own (free) Yelp API key, as using the one in the repository code will undoubtedly lead to readers collectively exceeding Yelp usage limits.

In Summary

Since Sencha Touch 2 is currently a preview, it does not yet have its complete and stable API. However, we've been able to show that with a small number of changes, it is possible to start using the framework now for existing applications. To do so, you need to be aware of, and discover, critical changes that have been made to the API.

Further, the class loader system, and other innovations, mean that there will often be better patterns you should start to use. The use of Ext.define() and Ext.create() are strongly recommended, for example.

Finally, be tolerant of the niggles introduced by a combination of a pre-release framework and assumptions that you might have made when developing against Sencha Touch 1.1. These are generally very easy to work around or solve. And if not, the Sencha forums remain a valuable place to log and discuss such bugs and issues.

This has been a brief introduction to some of the updates you should be aware of when using Sencha Touch 2 PR2, and hopefully you enjoy working with it and benefiting from the major enhancements these small changes on your part can bring.

Share this post:

Written by James Pearce
James Pearce heads developer relations at Sencha. He is a technologist, writer, developer and practitioner, who has been working with the mobile web for over a decade. Previously he was the CTO at dotMobi and has a background in mobile startups, telecoms infrastructure and management consultancy. James is the creator of tinySrc, the WordPress Mobile Pack, WhitherApps, modernizr-server and confess.js, and has written books on mobile web development for both Wiley and Wrox.Follow James on Twitter

“we’re removing map functionality”
Did you mention that map component was removed in ST2 ?

James Pearce
Sencha Employee

3 years ago

@Ram, no Ext.MAP has not been removed from the framework… it’s just there are some bugs with placing them inside tabs in the current Preview Release. It just simplified this article to leave that out for now.

Edmund Leung

3 years ago

@Mike, our goal is to release Touch 2 Beta by early next year or sooner. We will review our final release/GA date based on the feedback we get from the beta.

John

Tom Sawyer

3 years ago

Hi,

Sencha has lots of issues running on Android Devices. Not only is it sloppy, but also it does not render properly even in high end smart phones as Samsung Galaxy Ace. How are you going to address the issue.? In case that is not done, i don’t see point in investing time doing the learning.

James Pearce
Sencha Employee

3 years ago

@Tom, are you referring to Sencha Touch 1x or the 2 previews? We’ve spent most of this release focussed on Android performance, so it would be worth checking out the improvements. (The blog announcement includes some demo videos of just that. http://www.sencha.com/blog/sencha-touch-2-developer-preview/ )