定制自己的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属性

2012年7月24日 | 归档于 技术, 程序
本文目前尚无任何评论.

发表评论

XHTML: 您可以使用这些标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: