定制自己的ExtJS4的timefield控件
需求的变态是没有极限的
某RIA以前使用Silverlight开发,今年3月开始使用ExtJS4写JavaScript版本
两种完全不同的语言,写出来的界面任你再一致,很多特性还是很难移植的
比如Silverlight的timefield控件,支持用键盘的上下方向键调整时间,光标在小时数字的位置时,上下键的作用是增减一小时,光标在分钟数字的位置时,上下键的作用是增减一分钟
于是某测试人员说,既然Silverlight能做到,JS版本也希望实现这个功能
(余抽乃大爷100遍,这种要求何等无理啊,这是和控件自身相关的特性,JS要实现就非得hack ExtJS的代码不可,Silverlight能做到是控件它自身就已经实现,又不是代码写出来的,#$*%^&_+
花了一晚时间,还是把它给实现了,就是下面的定制timefield控件
由于光标位置没法判断,因此键盘操作只能增减一分钟
先是拦截timefield的keypress事件,不起作用,再试着拦截specialkey事件,UP键和DOWN键都成功捕捉到了
UP键的事件能停止,DOWN键的事件死活不能停止
仔细想一下,timefield控件的DOWN键是用来展开下拉列表的,看来默认是不给停止的了,没办法,读源代码
timefield是Ext.form.field.Time的别名
Ext.form.field.Time依次继承自Ext.form.field.Picker,Ext.form.field.Trigger,Ext.form.field.Text
在Ext.form.field.Picker中发现initEvents方法:
initEvents: function() { var me = this; me.callParent(); // Add handlers for keys to expand/collapse the picker me.keyNav = Ext.create('Ext.util.KeyNav', me.inputEl, { down: function() { if (!me.isExpanded) { // Don't call expand() directly as there may be additional processing involved before // expanding, e.g. in the case of a ComboBox query. me.onTriggerClick(); } }, esc: me.collapse, scope: me, forceKeyDown: true }); // Non-editable allows opening the picker by clicking the field if (!me.editable) { me.mon(me.inputEl, 'click', me.onTriggerClick, me); } // Disable native browser autocomplete if (Ext.isGecko) { me.inputEl.dom.setAttribute('autocomplete', 'off'); } },
很显然,Down键的事件在这里就被写死了
试着从Ext.form.field.Time派生一个新类MyProject.ui.UpDownTimeField,覆盖initEvents,绕过基类的事件处理
initEvents: function() { var me = this; me.callParent(); me.keyNav = Ext.create('Ext.util.KeyNav', me.inputEl, { down: function() { me.decreaseTime(); }, up: function(e) { me.increaseTime(); }, esc: me.collapse, scope: me, forceKeyDown: true }); if (!me.editable) { me.mon(me.inputEl, 'click', me.onTriggerClick, me); } if (Ext.isGecko) { me.inputEl.dom.setAttribute('autocomplete', 'off'); } },
还是失败了
细心一想,UpDownTimeField的initEvents方法中,me.callParent()调用基类方法,也即是Ext.form.field.Picker的initEvents,根本就还没绕过去嘛
于是用Ext.form.field.Text.prototype.initEvents.call(me)代替,成功用自己的事件处理取代了原来的事件处理
最后的UpDownTimeField:
Ext.define('MyProject.ui.UpDownTimeField', { extend: 'Ext.form.field.Time', alias: 'widget.updowntimefield', format: 'H:i', increment: 30, minValue: '00:00', maxValue: '23:59', initEvents: function() { var me = this; Ext.form.field.Text.prototype.initEvents.call(me); me.keyNav = Ext.create('Ext.util.KeyNav', me.inputEl, { pageDown: function() { if (!me.isExpanded) { me.onTriggerClick(); } }, down: function() { me.decreaseTime(); }, up: function(e) { me.increaseTime(); }, esc: me.collapse, scope: me, forceKeyDown: true }); if (!me.editable) { me.mon(me.inputEl, 'click', me.onTriggerClick, me); } if (Ext.isGecko) { me.inputEl.dom.setAttribute('autocomplete', 'off'); } }, increaseTime: function() { var me = this; if (me.isExpanded || !me.isValid()) { return; } var timeString = me.getRawValue(); if (timeString) { var arr = timeString.split(':'); var h = parseInt(arr[0], 10); var m = parseInt(arr[1], 10); m++; if (m > 59) { m = 0; h++; if (h > 23) { h = 0; } } h = h>=10 ? h.toString() : '0'+h; m = m>=10 ? m.toString() : '0'+m; me.setRawValue(h + ':' + m); } }, decreaseTime: function() { var me = this; if (me.isExpanded || !me.isValid()) { return; } var timeString = me.getRawValue(); if (timeString) { var arr = timeString.split(':'); var h = parseInt(arr[0], 10); var m = parseInt(arr[1], 10); m--; if (m < 0) { m = 59; h--; if (h < 0) { h = 23; } } h = h>=10 ? h.toString() : '0'+h; m = m>=10 ? m.toString() : '0'+m; me.setRawValue(h + ':' + m); } } });
使用:
{ xtype: 'updowntimefield', itemId: 'form-xxx-pickTimeField', name: 'LaunchTime', fieldLabel: message.YourLable, labelWidth: 150, labelClsExtra: 'your-css', regex: /^\d\d:[0-5]\d$/, regexText: message.YourMessage }
事实上timefield的合法性验证不是特别严密,可以自己指定regex属性
评论