Feels so good to be back here. Last time I blogged on my site, it was what, like 2015 ๐Ÿ˜ฎ
Have written intermittently on Imbibe’s blog since but really looking forward to writing and sharing more here again.

Meanwhile if you noticed, this site got a face-lift. A big shout-out and thanks to Gopesh for his help in making this happen.

Coming back to the topic of this blog post, we switched all our ExtJs based development from the classic to modern toolkit last year. The modern toolkit in ExtJs 6 has grown powerful enough to provide feature parity with the classic toolkit, additionally providing device interoperability within the same codebase which is a big plus. Add to the mix beautiful themes for ExtJs, and you have a very compelling and powerful toolkit to build device neutral enterprise grade web applications.

ExtJS 6.7.0 brought along the TimeField in the modern toolkit which was sorely missing from the earlier versions. Modern toolkit’s analogue watch TimeField is a league above to the Classic toolkit’s TimeField which is a plain Combobox with time intervals appearing in a drop-down. However missing in both toolkits is a built-in DateTime field to allow selection of Date and Time together in a single form field (which is very commonly required when filling in forms in intranet applications).
So sometime ago, I sat down tasking myself with producing a DateTime field for the modern toolkit whose result you can see in the form below (its a live form, go ahead and try selecting a date/time and clicking the button):

 


Basically I used the default Date and Time fields wrapping them in a FieldContainer and providing utility methods to get or set the value as a javascript Date object from both the fields. The complete code for the DateTimeField can be downloaded from here and is reproduced below for your reference:

Ext.define('Ext.field.DateTimeField', {
    extend: 'Ext.field.Container',
    xtype: 'datetimefield',

    initConfig: function (instanceConfig) {
        Ext.apply(instanceConfig, {
            items: [
                {
                    xtype: 'datefield',
                    required: Ext.isDefined(instanceConfig.required) ? instanceConfig.required : false,
                    submitValue: false,
                    flex: 1
                },
                {
                    xtype: 'timefield',
                    allowBlank: Ext.isDefined(instanceConfig.required) ? instanceConfig.required : false,
                    submitValue: false,
                    flex: 1
                }
            ]
        });

        Ext.applyIf(instanceConfig, {
            layout: {
                type: 'hbox',
                align: 'left'
            }
        });

        this.callParent(arguments);
    },

    getValue: function () {
        var dateField = this.getAt(0);
        var timeField = this.getAt(1);

        var date = dateField.getValue();
        if (date) {
            date = this.setTimePart(date, timeField.getValue());
        }

        return (date);
    },

    setValue: function (value) {
        var dateField = this.getAt(0);
        var timeField = this.getAt(1);

        if (value && Ext.isString(value)) {
            var tempValue = Imbibe.App.Date.parseDateTime(value);
            if (Ext.isDate(tempValue)) {
                value = tempValue;
            }
        }

        dateField.setValue(value);
        timeField.setValue(value);

        return (this);
    },

    getSubmitValue: function () {
        var me = this;

        return (this.serializeDateTime(me.getValue()));
    },

    parseDateTime: function (value, config) {
        if (Ext.isEmpty(value)) {
            return (value);
        } else {
            if (config && config.dateOnly) {
                return (Ext.Date.parse(value, 'Y-m-d'));
            } else {
                return (Ext.Date.parse(value, 'Y-m-d\\TH:i:sP'));
            }
        }
    },

    serializeDateTime: function (value, config) {
        if (Ext.isEmpty(value)) {
            return (value);
        } else {
            if (config && config.dateOnly) {
                return (Ext.Date.format(value, 'Y-m-d\\T00:00:00P'));
            } else {
                return (Ext.Date.format(value, 'Y-m-d\\TH:i:sP'));
            }
        }
    },

    getDatePart: function (date) {
        return (new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0));
    },

    setTimePart: function (d, t) {
        if (Ext.isEmpty(d)) {
            if (Ext.isEmpty(t)) {
                return (d);
            } else {
                d = new Date();
            }
        }
        d = this.getDatePart(d);

        if (!Ext.isEmpty(t)) {
            //Do not try to convert to a Date if time is already a date object.
            if (!Ext.isDate(t)) {
                if (t.length == 7)
                    t = '0' + t;

                t = Ext.Date.parse(t, 'h:i a');
            }

            d.setHours(t.getHours(), t.getMinutes(), 0, 0);
        }

        return (d);
    }

});

The code for the field itself is pretty straight-forward. However it has a few re-usable methods that might be of assistance at other places. These methods include:

  • parseDateTime / serializeDateTime
    Utility methods to convert a javascript Date object from/to universal serializable format for Date times.
  • getDatePart
    Method returns a Date object with time truncated from the passed-in Date object.
  • setTimePart
    Sets the time portion of a javascript Date object.

The following code was used to produce the example above:

Ext.application({
    name: 'Fiddle',

    launch: function () {
        Ext.Viewport.add({
            xtype: 'formpanel',
            referenceHolder: true,
            items: [
                {
                    xtype: 'datetimefield',
                    label: 'Select date/time',
                    reference: 'txtDateTime',
                    labelWidth: 200
                }
            ],
            buttons: [
                {
                    text: 'Click me',
                    handler: function () {
                        var txtDateTime = this.up('formpanel').lookupReference('txtDateTime');
                        Ext.Msg.alert('Date/time Value', txtDateTime.getSubmitValue());
                    }
                }
            ]
        });
    }
});

You can download the entire set of files for this example from the links below:

I hope you would find the DateTimeField useful. Do send your questions and/or suggestions using the comment form below.