Search code examples
extjs

Extjs Tab Panel tabchange event in modern toolkit


I am using ExtJS 7.5 Modern Toolkit. I had originally developed an application with a router in the classic toolkit based off the example shown below using a router and listening for tabchange events on the tab panel. We decided to change to the modern toolkit for the application, but I cant seem to figure out how to replicate the tabchange behavior as the tabchange event does not seem to exist in the modern toolkit.

The closest thing I was able to find was the activeitemchange event but that becomes a problem when using routes since when processing a route I need to update the active tab which triggers that event and subsequently cause another redirect to another route. The tabchange event was unaffected by setting the active item and only seemed to be affected by actually clicking / touching another tab.

Example in classic toolkit:

Ext.define('Router.controller.Navigation', {
    extend: 'Ext.app.Controller',

    config: {
        control: {
            '#main-tabs': {
                tabchange: 'onTabChange'
            },
            '#tab6': {
                tabchange: 'onChildTabChange'
            }
        },
        refs: {
            tabPanel: '#main-tabs'
        },
        routes: {
            'main-tabs:id:subid': {
                action: 'showTab',
                before: 'beforeShowTab',
                conditions: {
                    // take control of the :id & :subid parameters, make them optional but delimited by colon
                    ':id': '(?:(?::){1}([%a-zA-Z0-9\-\_\s,]+))?',
                    ':subid': '(?:(?::){1}([%a-zA-Z0-9\-\_\s,]+))?'
                }
            }
        }
    },

    onTabChange: function(tabPanel, newItem) {
        var id = newItem.getId(),
            child = newItem.child('tabpanel'),
            subid = '',
            hash = 'main-tabs:' + id;

        if (child) {
            newItem = child.getActiveTab();
            subid = ':' + newItem.getId();

            hash += subid;
        }

        this.redirectTo(hash);
    },

    onChildTabChange: function(tabPanel, newItem) {
        var parentTab = tabPanel.up(),
            parentId = parentTab.getId(),
            hash = 'main-tabs:' + parentId + ':' + newItem.getId();

        this.redirectTo(hash);
    },

    beforeShowTab: function(id, subid, action) {
        var tabPanel = this.getTabPanel(),
            child;

        if (!id) {
            // no id was specified, use 0 index to resolve child
            id = 0;
        }

        child = tabPanel.getComponent(id);

        if (child) {
            // tab found, resume the action
            action.resume();
        }
        else {
            Ext.Msg.alert('Tab Not Found', 'Tab with id or index "<b>' + id + '</b>" was not found!');

            // child not found, stop action
            action.stop();
        }
    },

    showTab: function(id, subid) {
        var tabPanel = this.getTabPanel(),
            child, childTabPanel;

        if (!id) {
            // no id was specified, use 0 index to resolve child
            id = 0;
        }

        child = tabPanel.getComponent(id);

        childTabPanel = child.child('tabpanel');

        tabPanel.setActiveTab(child);

        if (childTabPanel) {
            if (!subid) {
                subid = 0;
            }

            childTabPanel.setActiveTab(subid);
        }
    }
});

Ext.application({
    name: 'Router',

    requires: [
        'Ext.tab.Panel',
        'Ext.window.MessageBox'
    ],

    controllers: [
        'Navigation'
    ],

    defaultToken: 'main-tabs',

    launch: function() {

        Ext.create('Ext.tab.Panel', {
            renderTo: Ext.getBody(),
            id: 'main-tabs',
            height: 300,
            width: 600,
            activeTab: 0,
            defaults: {
                padding: 10
            },
            items: [
                {
                    title: 'Tab 1',
                    id: 'tab1',
                    layout: 'fit',
                    items: [
                        {
                            xtype: 'tabpanel',
                            id: 'tab6',
                            items: [
                                {
                                    title: 'Sub-tab 1',
                                    id: 'subtab1'
                                },
                                {
                                    title: 'Sub-tab 2',
                                    id: 'subtab2'
                                },
                                {
                                    title: 'Sub-tab 3',
                                    id: 'subtab3'
                                }
                            ]
                        }
                    ]
                },
                {
                    title: 'Tab 2',
                    id: 'tab2',
                    html: 'Tab 2 content'
                },
                {
                    title: 'Tab 3',
                    id: 'tab3',
                    html: 'Tab 3 content'
                },
                {
                    title: 'Tab 4',
                    id: 'tab4',
                    html: 'Tab 4 content'
                },
                {
                    title: 'Tab 5',
                    id: 'tab5',
                    html: 'Tab 5 content'
                }
            ]
        });

    }
});

Solution

  • Listening for the activeitemchange event, I would encounter an issue when using the back / forward browser buttons, handling the route, and using tabpanel.setActiveItem(). This would trigger the activeitemchange event causing another (unnecessary redirect).

    I instead used the beforeactiveitemchange event which allowed me to suspend the event in the handler when setting an active item so the redirect no longer occurred and the active item change still functioned as expected.

        control: {
            '#main-tabs': {
                beforeactiveitemchange: 'onMainTabChange'
            },
        }
    
        //Within the handler func that handles the route
        tabPanel.suspendEvent('beforeactiveitemchange');
        tabPanel.setActiveItem(tab);
        tabPanel.resumeEvent('beforeactiveitemchange');