View Full Version : Ext.ux.KeyMapPlugin

8 Jul 2011, 4:42 AM

I made this simplest of all plugins which often saves some typing. It's used to automatically add a keymap after component is rendered.

Ext.define('Ext.ux.KeyMapPlugin', {
init: function (cmp) {
cmp.mon(cmp, 'render', function (p) {
if (this._keyMap || !this.keyMap)
this._keyMap = this.el.addKeyMap(this.keyMap);
}, cmp);

It allows me to specify keymaps inside form configuration like this:

xtype: 'form',
plugins: new Ext.ux.FormKeyMapPlugin(),
keyMap: [{
key: Ext.EventObject.ENTER,
scope: this,
handler: this.doLogin
buttons: [{
text: 'Login',
formBind: true,
scope: this,
handler: this.doLogin

It would be great if keymaps are added by Ext automatically, but they are not, so plugin is required.

I'm interested to hear any suggestions.

Scott Murawski
9 Nov 2011, 7:23 PM
I was searching around for KeyMaps and found your post. I'm also interested in making things more automatic and decided a plugin is not automatic enough :)

The class definition preprocessor stuff is very nifty. In this case, if you have a keyMap property in your class definition, it'll be called. This phase is purely the class definition phase and you have no access to the constructed object (since no object is being constructed just yet). The trick is to use createInterceptor to hijack initComponent, so that way inside that function, you have access to the constructed object.

It key map either binds to the given elt property in the keyMaps cfg or to the object's own el property upon render, like the above plugin does.

// Automatically setup key mappings for every class
Ext.Class.registerPreprocessor("keyMap", function(cls, data, fn) {
var proto = cls.prototype;

// Set key maps on the constructed object (and not within the class prototype)
// This initComponent is called first, so you have access to the newly created this.keyMap.map
proto.initComponent = Ext.Function.createInterceptor(proto.initComponent, function () {

// The advantage of using this vs data is the keyMap can be monkied in the constructor
var keys = this.keyMap.keys;

// Set the default scope
for ( var i=0, len=keys.length; i < len; i++ ) {
keys[i].scope = keys[i].scope || this;

// The constructed object now has the actual map that can be used
if ( this.keyMap.elt ) {
this.keyMap.map = new Ext.util.KeyMap(this.keyMap.elt, keys);
} else {
this.on("render", function () {
this.keyMap.map = new Ext.util.KeyMap(this.el, keys);

// createInterceptor requires a true value to call the next fn in the chain
return true;
}).setDefaultPreprocessorPosition("keyMap", "last");

Similar to above, you can use a keyMap configuration like this

Ext.define("MyWidget", {
keyMap : {
elt : "my-element",
keys : [{
key : "l",
alt : true,
handler : function () {
console.log("hit alt-l");
}, {
key : "x",
alt : true,
handler : function () {
console.log("hit alt-x");

9 Nov 2011, 10:40 PM
That's great. Adding plugin all the time is tedious...

Scott Murawski
10 Nov 2011, 8:36 AM
I'm glad you like it. If you enhance it at all, I'd definitely be interested.

11 Nov 2011, 11:47 AM
If a KeyMap is required only on the Component el, then the getKeyMap() function (which exists for almost every widget) can be used in a simple override:

Ext.override(Ext.Component, {
afterRender: function () {
if(this.keys && this.getKeyMap && this.getKeyMap()) {

Then keys config can be specified like ExtJS3:

keys: [{
key: [10,13],
handler: this.submit,
scope: this
}, {
key: [27],
handler: this.cancel,
scope: this