ExtJS自定义控件 之一:datetimefield控件

这一年都在使用ExtJS 5.0开发系统,经常会遇到的一个场景就是时间日期的选择了。比如说,预定场地的时候就必须由用户选择预定的日期和时间。

可惜的是,ExtJS 5.0没有自带一个可以同时选择日期时间的控件(ExtJS 6.0已经推出了,还没用过,不知道加入这种控件没~),通常偷懒的做法就是,先放一个日期控件,然后再放一个时间控件。用户使用的时候,先选择日期,再选择时间,最后用很别扭的办法将日期和时间拼装起来。

理想的状态是,提供给用户一个可以同时选择时间和日期的控件,并且数据是“日期+时间”的Date类型。既符合使用习惯,又方便开发。

故第二种偷懒的方式是,从网上扒一个现成的解决方案来用。此法最快,但是在过程中遇到了一个问题,现成可以搜索到的方案都有点旧,大多数还停留在ExtJS 2.X、3.X和4.X版本,自从ExtJS从4升级到5之后,API改变了许多,所以并不能直接使用。

最后只能是自己写一个。准确来说,应该是拿了一份现成的代码修改,这个代码在ExtJS 5.0下不能正确运行,但是需要修改的量非常少,所以很感谢这份现成代码的作者,可惜没有署名,不知道是谁。

ExtJS 5.0上测试成功,实现方法是:

1、自定义一个时间选择器TimePickerField,继承自Ext.form.field.Base,由三个NumberField组成。

    Ext.define(‘MyApp.ux.DateTimeField.TimePickerField‘, {
        extend: ‘Ext.form.field.Base‘,
        alias: ‘widget.timepicker‘,

        alternateClassName: ‘Ext.form.field.TimePickerField‘,

        requires: [‘Ext.form.field.Number‘],
        inputType: ‘text‘,
        fieldLabel: ‘时间‘,
        labelWidth: 40,
        style: ‘padding:4px 0; margin: 0; ‘,
        value: null,
        spinnerCfg: {
            width: 50,
        },

        initComponent: function() {
            var me = this;
            me.value = me.value || Ext.Date.format(new Date(), ‘H:i:s‘);
            me.callParent(arguments);
            me.spinners = [];
            var cfg = Ext.apply({}, me.spinnerCfg, {
    //          readOnly: me.readOnly,
                disabled: me.disabled,
                style: ‘float: left‘,
                listeners: {
                  change: {
                    fn: me.onSpinnerChange,
                    scope: me
                  }
                }
            });
            me.hoursSpinner = Ext.create(‘Ext.form.field.Number‘, Ext.apply({}, cfg, {
    //          minValue: -1,
    //          maxValue: 24,
                    minNum: 0,
                    maxNum: 23,
              }));
            me.minutesSpinner = Ext.create(‘Ext.form.field.Number‘, Ext.apply({}, cfg, {
    //          minValue: -1,
    //          maxValue: 60,
                        minNum: 0,
                        maxNum: 59,
              }));
            me.secondsSpinner = Ext.create(‘Ext.form.field.Number‘, Ext.apply({}, cfg, {
    //          minValue: -1,
    //          MAXVALUE: 60,
                        minNum: 0,
                        maxNum: 59,
              }));
            me.spinners.push(me.hoursSpinner, me.minutesSpinner, me.secondsSpinner);
        },

        onRender: function() {
            var me = this, spinnerWrapDom, spinnerWrap;
            me.callParent(arguments);
            spinnerWrap = Ext.get(Ext.DomQuery.selectNode(‘div‘, this.el.dom));
            me.callSpinnersFunction(‘render‘, spinnerWrap);
            this.el.dom.getElementsByTagName(‘input‘)[0].style.display = ‘none‘;
            var newTimePicker = Ext.DomHelper.append(spinnerWrap, {
                tag: ‘div‘,
                cls: ‘x-form-clear-left‘
            }, true);
            this.setRawValue(this.value);
        },
        _valueSplit: function(v) {
            if(Ext.isDate(v)) {
              v = Ext.Date.format(v, ‘H:i:s‘);
            }
            var split = v.split(‘:‘);
            return {
                h: split.length > 0 ? split[0] : 0,
                m: split.length > 1 ? split[1] : 0,
                s: split.length > 2 ? split[2] : 0
            };
        },
        onSpinnerChange: function() {
            if(!this.rendered) {
              return;
            }
            //限制时间范围
            var args = arguments; //this, newValue, oldValue, eOpts
            args[0].setValue( (args[1]>args[0].maxNum) ? args[0].minNum : args[0].value );
            args[0].setValue( (args[1]<args[0].minNum) ? args[0].maxNum : args[0].value );
            this.fireEvent(‘change‘, this, this.getValue(), this.getRawValue());
        },

        // 依次调用各输入框函数, call each spinner‘s function
        callSpinnersFunction: function(funName, args) {
            for(var i = 0; i < this.spinners.length; i++) {
                if( this.spinners[i][funName] != null && this.spinners[i][funName] != undefined ){
                    this.spinners[i][funName](args);
                }
            }
        },

        getRawValue: function() {
            if(!this.rendered) {
                var date = this.value || new Date();
                return this._valueSplit(date);
            }
            else {
                return {
                h: this.hoursSpinner.getValue(),
                m: this.minutesSpinner.getValue(),
                s: this.secondsSpinner.getValue()
                };
            }
        },
        setRawValue: function(value) {
            var v = this._valueSplit(value);
            if(this.hoursSpinner) {
                this.hoursSpinner.setValue(v.h);
                this.minutesSpinner.setValue(v.m);
                this.secondsSpinner.setValue(v.s);
            }
        },

        getValue: function() {
            var v = this.getRawValue();
            return Ext.String.leftPad(v.h, 2, ‘0‘) + ‘:‘ + Ext.String.leftPad(v.m, 2, ‘0‘) + ‘:‘
            + Ext.String.leftPad(v.s, 2, ‘0‘);
        },

        setValue: function(value) {
            this.value = Ext.isDate(value) ? Ext.Date.format(value, ‘H:i:s‘) : value;
            if(!this.rendered) {
                return;
            }
            this.setRawValue(this.value);
            this.validate();
        },

        disable: function() {
            this.callParent(arguments);
            this.callSpinnersFunction(‘disable‘, arguments);
        },

        enable: function() {
            this.callParent(arguments);
            this.callSpinnersFunction(‘enable‘, arguments);
        },

        setReadOnly: function() {
            this.callParent(arguments);
            this.callSpinnersFunction(‘setReadOnly‘, arguments);
        },

        clearInvalid: function() {
            this.callParent(arguments);
            this.callSpinnersFunction(‘clearInvalid‘, arguments);
        },

        isValid: function(preventMark) {
            return this.hoursSpinner.isValid(preventMark) && this.minutesSpinner.isValid(preventMark)
            && this.secondsSpinner.isValid(preventMark);
        },

        validate: function() {
            return this.hoursSpinner.validate() && this.minutesSpinner.validate() && this.secondsSpinner.validate();
        }
    });

2、自定义一个DateTimePicker,继承自Ext.picker.Date,实际上就是在原有的DateField的基础上增加TimePicker和一个确认按键。

    Ext.define(‘MyApp.ux.DateTimeField.DateTimePicker‘, {
        extend: ‘Ext.picker.Date‘,
        alias: ‘widget.datetimepicker‘,
        requires: [‘MyApp.ux.DateTimeField.TimePickerField‘,‘Ext.dom.Query‘],

        todayText: ‘现在‘,
        timeLabel: ‘时间‘,
        buttonText: ‘确定‘,

        initComponent: function() {
            this.callParent();
            this.value = this.value || new Date();
        },
        onRender: function(container, position) {
            this.callParent(arguments);
            var me = this;

            //确认按键
            var btnCfg = Ext.apply({}, {}, {
                style: ‘center‘,
                listeners: {
                    click: {
                        fn: function(){
                            this.confirmDate();
                        },
                        scope: me
                    }
                }
                });
            me.confirmBtn = Ext.create(‘Ext.Button‘, Ext.apply({}, btnCfg, {
                text: ‘确认‘,
            }));
            me.confirmBtn.render(this.el.child(‘div div.x-datepicker-footer‘));

            if(!this.timefield) {
                this.timefield = Ext.create(‘MyApp.ux.DateTimeField.TimePickerField‘, {
                    fieldLabel: this.timeLabel,
                    labelWidth: 40,
                    value: Ext.Date.format(this.value, ‘H:i:s‘),
                });
            }

            this.timefield.ownerCt = this;//指定范围
            this.timefield.on(‘change‘, this.timeChange, this);//

            var table = Ext.get(Ext.DomQuery.selectNode(‘table‘, this.el.dom));

            var tfEl = Ext.DomHelper.insertAfter(table, {
                tag: ‘div‘,
                style: ‘border:0px;‘,
                children: [{
                    tag: ‘div‘,
                    cls: ‘x-datepicker-footer ux-timefield‘
                }]
            }, true);
            this.timefield.render(this.el.child(‘div div.ux-timefield‘));

            var p = this.getEl().parent(‘div.x-layer‘);
            if(p) {
                p.setStyle("height", p.getHeight() + 31);
            }
        },
        // listener 时间域修改, timefield change
        timeChange: function(tf, time, rawtime) {
            this.value = this.fillDateTime(this.value);
        },
        fillDateTime: function(value) {
            if(this.timefield) {
                var rawtime = this.timefield.getRawValue();
                value.setHours(rawtime.h);
                value.setMinutes(rawtime.m);
                value.setSeconds(rawtime.s);
            }
            return value;
        },
        changeTimeFiledValue: function(value) {
            this.timefield.un(‘change‘, this.timeChange, this);
            this.timefield.setValue(this.value);
            this.timefield.on(‘change‘, this.timeChange, this);
        },
        setValue: function(value) {
            this.value = value;
            this.changeTimeFiledValue(value);
            return this.update(this.value);
        },
        getValue: function() {
            return this.fillDateTime(this.value);
        },
        handleDateClick: function(e, t) {
            var me = this,
                handler = me.handler;
            e.stopEvent();
            if(!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)) {
                me.doCancelFocus = me.focusOnSelect === false;
                me.setValue(this.fillDateTime(new Date(t.dateValue)));
                delete me.doCancelFocus;
                me.fireEvent(‘select‘, me, me.value);
                if(handler) {
                handler.call(me.scope || me, me, me.value);
                }
                me.onSelect();
            }
        },
        //确认按键
        confirmDate: function(){
                var that = this;
                    that.fireEvent(‘select‘, that, that.value);//模拟用户选择
                    that.onSelect();
        },
        selectToday: function() {
            var me = this,
                btn = me.todayBtn,
                handler = me.handler;
            if(btn && !btn.disabled) {
                me.setValue(new Date());
                me.fireEvent(‘select‘, me, me.value);
                if(handler) {
                handler.call(me.scope || me, me, me.value);
                }
                me.onSelect();
            }
            return me;
        }
    });

3、将DateTimePicker装载到Ext.form.field.Date中,初始化日期时间的格式。

    Ext.define(‘MyApp.ux.DateTimeField.DateTimeField‘, {
            extend: ‘Ext.form.field.Date‘,
            alias: ‘widget.datetimefield‘,
            requires: [‘MyApp.ux.DateTimeField.DateTimePicker‘],
            initComponent: function() {
                this.format = this.format;
                this.callParent();
            },
            format: ‘Y-m-d H:i:s‘,
            createPicker: function() {
                var me = this,
                        format = Ext.String.format;
                return Ext.create(‘MyApp.ux.DateTimeField.DateTimePicker‘, {
                        ownerCt: me.ownerCt,
    //                  renderTo: document.body,
                        floating: true,
    //                  hidden: true,
                        focusOnShow: true,
                        minDate: me.minValue,
                        maxDate: me.maxValue,
                        disabledDatesRE: me.disabledDatesRE,
                        disabledDatesText: me.disabledDatesText,
                        disabledDays: me.disabledDays,
                        disabledDaysText: me.disabledDaysText,
                        format: me.format,
                        showToday: me.showToday,
                        startDay: me.startDay,
                        minText: format(me.minText, me.formatDate(me.minValue)),
                        maxText: format(me.maxText, me.formatDate(me.maxValue)),
                        listeners: {
                                scope: me,
                                select: me.onSelect
                        },
                        keyNavConfig: {
                                esc: function() {
                                    me.collapse();
                                }
                        }
                    });
            }
    });

将以上代码拷到ux当中,调用的时候可以Ext.create之,也可以require之后直接使用alias。

效果嘛,就像下图那样:

介绍完毕。

谢谢大家。

时间: 2024-10-06 17:13:44

ExtJS自定义控件 之一:datetimefield控件的相关文章

自定义控件VS用户控件

1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Controls控件 自定义控件(Custom Control):继承自 Control,主要用于开发windows控件的最基本的类,比如 Text,Button 控件 2 要开发自己的控件的几种方法[1] 复合控件(Composite Controls):将现有的各种控件组合起来,形成一个新的控件,来满

winform 自定义控件:半透明Loading控件

winform  自定义控件:半透明Loading控件 by wgscd date:2015-05-05 效果: using System;using System.Drawing;using System.Windows.Forms;using System.ComponentModel;using System.Threading; namespace wgscd{ /// <summary> /// 自定义控件:半透明控件 /// </summary> [ToolboxBit

ExtJs内的datefield控件选择日期过后的事件监听select

[摘要]: 选择时间过后我们为什么需要监听事件?一般有这样一种情况,那就是用于比较两个时间大小或者需要判断在哪个时间点上需要做什么样的操作.基于这样的种种情况,我们很有必要琢磨一下datefield控件的日期选择事件了的. 那么我们如何添加日期选择事件呢?针对这样一个问题,网上有很多ExtJs的盆友想到了change事件,就是当文本框内日期值就上一次值来说不同时,触发该事件,change事件添加核心代码如下所示: { xtype: 'datefield', name: 'birthday', f

Android自定义控件之日历控件

Android自定义控件之日历控件 2015-10-23 Android开发中文站 三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽.所以自己仍需继续努力.学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件.效果图先来一发. 我们分析下效果图,然后确定我们的需求. (1).绘制星期的自定义View,用于标识日期的礼拜. (2).绘制日期的自

C# 自定义控件VS用户控件

1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Controls控件 自定义控件(Custom Control):继承自 Control,主要用于开发windows控件的最基本的类,比如 Text,Button 控件 2 要开发自己的控件的几种方法[1] 复合控件(Composite Controls):将现有的各种控件组合起来,形成一个新的控件,来满

EXTJS 3.0 资料 控件之 html 潜入label用法

这是在Extjs 中插入html 控件label! html: "<div><label id='howMany'>您共选中了</label><br/></div>"获取label值:document.getElementById('howMany').innerText赋值label值:document.getElementById('howMany').innerText = "这是赋值"; docum

【自定义控件】组合控件

组合控件是自定义控件的一种,只不过它是由其他几个原生控件组合而成,故名组合控件. 在实际项目中,GUI会遇到一些可以提取出来做成自定义控件情况. 一个自定义控件的好处就是把一些需要模块化的UI和逻辑放在一起,做到了高内聚,向其他模块提供接口并很少 依赖外界,这样就是低耦合.一个自定义控件就是一个封闭的王国,这里由你掌控. 上述是我自己的一个体会,想必大家也会常做自定义控件吧,就像逻辑部分的模块化一样. 下面我要做一个例子,请看完成图. 下面一排图片加文字就是组合控件了,我是怎么做的呢? 其实这里

自定义控件之--继承控件(圆形TextView)

师从郭大,自学于心,继承控件无疑就是继承自现有控件,保持继承的控件的属性并进行必要的扩展. 比如下面这个自定义控件,它就保持了TextView的属性,并对TextView的外观进行必要的修改该来适应自己的项目,主要实现的是一个圆形的TextView的功能. 三个构造方法: onMesure():onMeasure()方法顾名思义就是用于测量视图的大小的,确定视图的宽度和高度的规格和大小; draw():视图绘制,canvas.drawCircle(getWidth()/2, getHeight(

【安卓自定义控件系列】安卓自定义控件之组合控件

在安卓开发中,谷歌已经为我们提供了许多原生控件,基本上能够满足我们日常的开发需求,但是某些项目中原生控件可能达不到产品所要求的各式各样的酷炫效果或功能效果,这个时候我们只能自己自定义控件来满足项目需求,我们知道自定义控件包括三种方式: 1继承控件,即继承谷歌提供的原生控件,在此基础上提供一些原生控件不具备的功能,如github上各种酷炫效果的开源组件基本上都是采用的这种方式. 2组合控件:即组合多个原生控件来达到某些单个原生控件原本不具备的功能,这个在日常开发中应该是使用的比较多的,如基本上每个