Data

DataViews, such as Lists, display the content of Stores. Stores are generally backed by Proxies, which do CRUD operations on items against various backends, including server-side APIs and local storage objects.

Proxies implement their

Gotchas

Fullscreen and card layouts

Sencha's docs say to only have one Component in an application have fullscreen: true: the viewport master container. If you have multiple children of the viewport and a card layout on it so only one child shows at a time, each should render fullscreen.

However, in some cases this breaks down. For instance, calling doComponentLayout() on a Panel with a vertically docked sidebar after the render event of the Panel causes the height of the sidebar to be recomputed as the height of the HTML content of the Panel itself.

Setting fullscreen: true on the panel seems to fix this.

See also http://www.sencha.com/forum/showthread.php?127885-TabPanel-won-t-render-items-unless-fullscreen-set-to-true

However, fullscreen affects the way rendering works. See http://stackoverflow.com/questions/4578936/ext-layout-cardlayout-requires-fullscreen

One hack that seems to work is to explicitly set the component's width and height to match the viewport's, similar to what Ext.Component does if it detects fullscreen: true:

PFSPanel = Ext.extend(Ext.Panel, {
    initComponent: function () {
        var viewportSize;
        if (this.pseudoFullscreen) {
            viewportSize = Ext.Viewport.getSize();
            this.width = viewportSize.width;
            this.height = viewportSize.height;
        }
        PFSPanel.superclass.initComponent.call(this);
    }
}

It seems as though this would require setting this.monitorOrientation as well, but it doesn't seem to and I haven't investigated.

Scrolling

You must set 'flex' on scrollable panels which are contained within a vbox/stretch layout in order for their heights to be computed correctly for scrolling. Symptoms of not doing this are scroll panels which "spring back" to their initially visible components as soon as released.

Maps

Wait for the Ext.Map's maprender event before attempting to get a reference to the Google map for use with the Google Maps JS API.

Implement a "stationary" center point pin with:

mapCmp.on({
    maprender: function () {
        var map, marker;
        map = mapCmp.map;
        marker = new google.maps.Marker({
            position: map.getCenter(), 
            map: map
        });
        google.maps.event.addListener(
            map, 
            'center_changed', 
            function () { 
                marker.setPosition(map.getCenter()); 
            }
        );
    }
});

The pin does lag a bit, especially while dragging on mobile, so it may be better to simply overlay a graphic over the map.

Scroll a List

mylist.scroller.scrollTo({ x: 0, y: 0 });

See "Reset list scroll position to top" on the Sencha Forums.

TabPanels and MVC

TabPanels make setting up individual tabs via MVC controllers difficult because tapping a tab button directly activates the corresponding tab, with no opportunity to hook in a controller action. There's also no event that corresponds to the logic performed in the TabBar's onTabTap handler and the TabPanel card layout's card switch methods. This means there is no way to fire a controller action if and only if a tab is switched to due to a tab button press.

It is possible to listen for a tab's activate event. However, this will be called when the tab is switched to for any reason. This event can't thus be mapped directly to a default controller action, since the default would then override a "deep link" whenever the tab was activated.

Triggering a default action on tab activate only when there is nothing better to show:

// 'tab' is a Panel with a card layout within a TabPanel
var tab = Ext.getCmp('mytab');
tab.on.({
    activate: function () {
        if (tab.items.length === 0 || tab.getActiveItem() === null) { 
            // run default controller if (1) tab is empty or (2) tab has nothing selected
            // this requires that the default action create the default panel if it does not exist
            // and that unused 'subpanels' are destroyed when closed since there will be no
            // positive way to trigger the default controller
            Ext.dispatch({
                controller: 'MyController',
                action: 'default'
            });
        }
    }
});

Sencha Touch 1.1.0

Seems not to like the disabling of non-iOS animations used in several of the 1.0.1a demos.

The sass for themeing shipped with 1.1.0 also requires an updated version of compass (which will not play nicely with the 1.0.1a sass).

Fancy List plugins

ListPullRefresh and ListOptions are available as pluings.

ListPullRefresh can be used to refresh the contents of a List DataView when the user pulls down the list past the first item.

ListOptions allows displaying a menu of actions to perform on a per list item basis by swiping on a particular list item.

Video Tutorials

Sencha's written documentation is mostly limited to raw API documentation and a few demos and example walkthroughs. Most of the best practices are documented only in Sencha's videos on Vimeo and the corresponding slides and code downloads.

PhoneGap โ€“ Android

Orientation

On Android, orientation changes can be limited via the AndroidManifest.xml file. Set the android:screenOrientation attribute of the application node.

Locking the orientation completely generally isn't a great idea, since that will prevent the app from rotating even when using landscape-oriented hardware keyboards. To prevent orientation changes based on tilting the phone but still allow landscape orientation for hardware keyboard, set android:ScreenOrientation="nosensor".

Soft keyboard

The Android soft keyboard can either overlay the application or shrink the application to fit the keyboard on screen. This is controlled by the android:windowSoftInputMode attribute.

Sencha/PhoneGap apps seem to do better with panning: android:windowSoftInputMode="stateUnspecified|adjustPan".