Better Declarative Listeners for ExtJS

September 10, 2014

The most recent post on the Sencha Blog, Declarative Listeners in Ext JS 5, goes over some of the improvements made in the latest ExtJS release to make attaching event listeners to components a little easier. I have been using a small override in ExtJS 4 for a while that offers a similar declarative approach to attaching listeners, but also gives the developer the ability to traverse the component hierarchy or listen to stores. This small override completely changed the way I develop apps using ExtJS.

Usage

The code exposes one new config option on all components: listens. It is a hash of ‘componentselector@event’ paired with a function or function name to be attached when the component is rendered. For example:


Ext.define('MyClass', {
    extend: 'Ext.Panel',

    listens: {
        '#btn-save@click': 'onSave',
        '#btn-cancel@click': 'onCancel'
    },

    onSave: function(){
    },

    onCancel: function(){
    }
});

The code above listens for a “click” event on 2 child components with an itemId: btn-save and btn-cancel. Like the new declarative listeners in ExtJS 5, member functions can be specified as a string.

Going up the component hierarchy

By default, selectors are resolved using component.down(selector). To go up the component hierarchy instead of down, prepend a ^ to your selector(s). This can be useful, for example, when child components need to respond to a parent component being shown.


Ext.define('MyClass', {
    extend: 'Ext.Panel',
    itemId: 'mainCt',
    items: [{
        xtype: 'dataview',
        ...
        listens: {
            '^#mainCt@show': 'reloadView'
        },
        reloadView: function(){
        }
    }]
});

Multiple selectors are also supported by using a comma:


Ext.define('MyClass', {
    extend: 'Ext.Panel',
    listens: {
        '#btn-save@click,#btn-load@click': 'showLoading'
    },

    showLoading: function(){
    }
});

Listeners can be attached to the component itself using this:


Ext.define('MyClass', {
    extend: 'Ext.Panel',
    listens: {
        'this@boxready': 'onReady'
    },
    onReady: function(){
    }
});

Store Listeners

To attach a listener to an ExtJS Store, use $storeId.


Ext.define('MyClass', {
    extend: 'Ext.Panel',
    listens: {
        '$myStore@beforeload': 'showLoading'
    },
    showLoading: function(){
    }
});

Notes

  • All handlers are attached using mon() and are automatically removed when the listening component is destroyed.
  • Selector results are cached internally while handlers are being attached so the same selector can be used multiple times with no performance hit.
  • Listeners are attached in the afterRender method and won’t fire until the component is rendered.
  • Listeners are merged automatically when extending a class. To prevent inheriting the listens configuration from the super class, set inheritListens to false on the child class.

Download

The code is available as a gist here. It’s not beautiful looking code but it works great with ExtJS 4 and may work with ExtJS 5 (untested). Cheers!