Dynamically Updating Badges in Sencha Touch

We're using Sencha Touch as our mobile hybrid development framework in a current project for a client.  One of the more challenging issues we ran into was with updating the badges in the tab bar when using Ext.tab.Panel.  Here's what we found along with our solution.

Setting up the Badge Text


Ext.tab.Panel comes with a feature to create a tab bar when an item is added.  This happens implicitly on initialization in this sample code below.

Ext.define('Project.view.phone.Main', {
    extend: 'Ext.tab.Panel',
    xtype: 'main',
    config: {
        tabBar: {
            docked: 'bottom'
        },
        items: [
            {
            type: 'Ext.NavigationView',
            id: 'news',
                title: 'News',
                iconCls: 'news'
            }, {
            type: 'Ext.NavigationView',
            id: 'mail',
                title: 'Mail',
                iconCls: 'mail'
            }
        ]
    }
});

The below screenshot is what is rendered to the screen.


Now, let's say we want to alert the user of how many items are in the view of News items.  One way to do this is to add a badge to the item, like this:

Ext.define('Project.view.phone.Main', {
    extend: 'Ext.tab.Panel',
    xtype: 'main',
    config: {
        tabBar: {
            docked: 'bottom'
        },
        items: [
            {
            type: 'Ext.NavigationView',
            id: 'news',
                title: 'News',
                iconCls: 'news',
                badgeText: '1'
            }, {
            type: 'Ext.NavigationView',
            id: 'mail',
                title: 'Mail',
                iconCls: 'mail'
            }
        ]
    }
});

This will produce a badge, like below.


Now, we want to update this badge as our store refreshes with data.  Our approach was to try something like below.

Ext.define('Project.view.phone.Main', {
    extend: 'Ext.tab.Panel',
    xtype: 'main',
    config: {
        tabBar: {
            docked: 'bottom'
        },
        listeners: {
            initialize: function () {
                Ext.getStore('news').addListener({
                    refresh: {
                        fn: this.updateBadgeText,
                        scope: this
                    }
                });
            }
        },
        items: [
            {
            type: 'Ext.NavigationView',
            id: 'news',
                title: 'News',
                iconCls: 'news',
                badgeText: '1'
            }, {
            type: 'Ext.NavigationView',
            id: 'mail',
                title: 'Mail',
                iconCls: 'mail'
            }
        ]
    },
    updateBadgeText: function () {
        var count = Ext.getStore('news').getCount();

        // This line tells us the initial value for badge text.
        this.items.getByKey('news').badgeText;

        // This next line won't work.
        this.items.getByKey('news').setBadgeText(count);
    }
});

Here, we added a listener to our store to update the badge text whenever the store is refreshed.  That didn't work in our case, since setBadgeText is not a function of the News item.  There is a value there, and you can reference it by calling this.items.getByKey('news').badgeText, but without a getter or setter to there is no connection to the DOM element displaying the badge.

Updating the Badge Text Dynamically


We found the answer in the source code.  If you look at Panel.js you can see when an item is added a tab bar is created in-memory.  The creation includes this line:

tabBadgeText = (card.getBadgeText) ? card.getBadgeText() : initialConfig.badgeText

This copies the value of the badge text on run time to the in-memory tab bar used for Ext.tab.Panel.  Since the tab is created in-memory, the IDs of each item are automatically assigned.  Because of this, one solution is to simply change updateBadgeText() to the below code:

updateBadgeText: function () {
    var count = Ext.getStore('news').getCount();
    // This line will work
    this.getTabBar().items.getByKey('ext-tab-1').setBadgeText(count);
}

Another approach would be for Ext.NavigationView, or any other container with badge text, to raise an event when the badge text is updated.  Then, the in-memory tab bar could listen to these events.  In this case, we could update the badge text of the navigation view and trust the tab panel to handle the representation in the tab bar.

Sencha Touch is always improving, and is making it significantly easier for us to deliver a single, unified code base that supports multiple device platforms.  Our clients love this, because it cuts back a great deal of the development costs with maintaining separate code bases for iOS and Android.

Labels: , , , ,