DOM event handling in Ext JS MVC

June 19, 2013

Sometimes you need to work with DOM elements in your custom Components, or stock Ext JS Components do not fire events in certain conditions that you would like to react to in your Controllers. What do you do in such case? You can refire DOM event as Component event.

Suppose you want Panel’s `mouseover` events to be controlled MVC style. This is how it could look like:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    listeners: {
        mouseover: {
            element: 'el',
            fn: function(event) {
                this.fireEvent('myPanelMouseOver', this);
            }
        }
    }
});

… except that it would not work, because `this` scope in event handler will be set to the `Ext.dom.Element` instance of our Panel’s main element, instead of the Panel itself! To make it work, we should find the Component to which the Element belongs, and fire event from that Component:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    listeners: {
        mouseover: {
            element: 'el',
            fn: 'onElMouseover'
        }
    },

    onElMouseover: function(event) {
        // In this case, we can easily find the Component,
        // because el will have the same id as the Panel.
        // Performance will not be a problem either
        // since Ext.getCmp() lookups are very fast.
        var cmp = Ext.getCmp(this.id);

        // Now we can fire a Component event that Controllers
        // will see and can react to.
        if (cmp) {
            cmp.fireEvent('myPanelMouseover', cmp, event);
        }
        // The code between <debug></debug> tags will be removed
        // from production code by Sencha Cmd compiler, so this
        // is "dev eyes only"
        //<debug>
        else {
            Ext.Error.raise('mouseover handler has Element ' +
                            'with id ' + this.id + ', but no ' +
                            'matching Component found!');
        }
        //</debug>
    }
});

This approach will be beneficial in simpler cases, where relationship between DOM Element and Component is straightforward and it is easy to find Component by its Element. However there are times when it is not so; in such case we will have to capture the scope (Component) and reuse it:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.panel.Panel',

    listeners: {
        afterrender: function() {
            // Monitored listener does not need to be explicitly destroyed,
            // Component destructor takes care of it
            this.mon(
                // This time we want only Panel body to report mouseover
                this.body, 'mouseover',

                // Event handler
                function(event) {
                    this.fireEvent('myPanelMouseOver', this, event);
                },

                // Finally, set the scope for event handler
                this
            );
        }
    }
});

Another good example is Action column in a Grid: it allows adding action icons, like ‘edit’ or ‘delete’ to perform corresponding operations on a row; however it is not controllable by default. In such case we can use `handler` config option and refire the events we need:

Ext.define('MyApp.view.MyPanel', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.mypanel',

    columns: [{
        xtype: 'actioncolumn',
        items: [{
            icon: 'edit.png',
            handler: function(grid, rowIndex) {
                var record = grid.getStore().getAt(rowIndex);
                grid.fireEvent('editAction', grid, record);
            }
        }, {
            icon: 'delete.png',
            handler: function(grid, rowIndex) {
                var record = grid.getStore().getAt(rowIndex);
                grid.fireEvent('deleteAction', grid, record);
            }
        }]
    }]
});

Ext.define('MyApp.controller.MyPanel', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            mypanel: {
                editAction: 'onEditAction',
                deleteAction: 'onDeleteAction'
            }
        });
    },

    onEditAction: function(grid, record) {
        ...
    },

    onDeleteAction: function(grid, record) {
        ...
    }
});

Of course the handlers can be abstracted further, they are repeated here for the sake of clarity.

tags: , , ,
posted in Software development by nohuhu

Follow comments via the RSS Feed | Leave a comment | Trackback URL

Leave Your Comment

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org