Currently we are using Sencha Touch as the basis for our new splog.me app.The Sencha Touch 1.1 framework includes a very large number of readily available form fields including the DatePicker form field and DatePicker popup sheet itself. Strangely enough, a TimePicker is missing so we decided to build one ourselves. The TimePicker below is heavily based on the DatePicker classes and includes a form field and a picker.

 

 

 

Ext.ux.form.TimePicker

To quickly demonstrate, the screenshots below show a TimePicker without any custom configuration and one that only shows a certain timespan and shows every fifth minute.

Usage

The TimePicker can be inserted into a form using it’s xtype ‘timepickerfield’ or by creating an instance yourselve.

	var startTime = new Ext.ux.form.TimePicker({
		name : 'starttime',
		label: 'Start time'
	});

Or it can be customized with the starting hour, ending hour and minute interval:

	var startTime = new Ext.ux.form.TimePicker({
		name : 'starttime',
		label: 'Start time',
		minuteScale: 5,
		hourFrom: 8,
		hourTo: 18,
		value: {
			hour: (new Date().getHours()+1)%24, /* start with the next hour */
			minute: 0
		}
	});

Feel free to use the timepicker in your Sencha Touch app and let me know when you do. Always nice to see other great mobile apps. If  you think of any improvements (EG. making it configurable for 24h/ AM PM format), let me know also. The code is displayed below and attached to this post as a tgz archive.

Cheers,
Rob

Code

Download both classes

Ext.ux.Form.TimePicker

Ext.ns('Ext.ux.form');
/**
 * @class Ext.ux.form.TimePicker
 * @extends Ext.form.Field
 *
Specialized field which has a button which when pressed, shows a {@link Ext.ux.TimePicker}.

 * @xtype timepickerfield
 */
Ext.ux.form.TimePicker = Ext.extend(Ext.form.Field, {
    ui: 'select',

	/**
     * @cfg {Number} minuteScale
     * List every how many minutes, eg. 5 lists 0, 5, 10, 15, etc. Defaults to 1
     */
    minuteScale: 1, 

	/**
     * @cfg {Number} hourFrom
     * The start hour for the time picker.  Defaults to 0
     */
    hourFrom: 0,

    /**
     * @cfg {Number} hourTo
     * The last hour for the time picker.  Defaults to 23
     */
    hourTo: 23,

	/**
     * @cfg {Object/Ext.ux.TimePicker} picker
     * An object that is used when creating the internal {@link Ext.ux.TimePicker} component or a direct instance of {@link Ext.ux.TimePicker}
     * Defaults to null
     */
    picker: null,

	/**
     * @cfg {Object/Time} value
     * Default value for the field and the internal {@link Ext.ux.TimePicker} component. Accepts an object of 'hour',
     * and 'minute' values, all of which should be numbers, or a Time string.
     *
     * Example: {hour: 18, minute: 15} = 18:15
     */
	/**
     * @cfg {Boolean} destroyPickerOnHide
     * Whether or not to destroy the picker widget on hide. This save memory if it's not used frequently,
     * but increase delay time on the next show due to re-instantiation. Defaults to false
     */
    destroyPickerOnHide: false,

    // @cfg {Number} tabIndex @hide

    // @cfg {Boolean} useMask @hide

    // @private
    initComponent: function() {
        this.addEvents(

			/**
             * @event change
             * Fires when a Time is selected
             * @param {Ext.ux.form.TimePicker} this
             * @param {Time} Time The new Time
             */
            'change'
        );

        this.tabIndex = -1;
        this.useMask = true;

        Ext.form.Text.superclass.initComponent.apply(this, arguments);
    },

	/**
     * Get an instance of the internal Time picker; will create a new instance if not exist.
     * @return {Ext.ux.TimePicker} TimePicker
     */
    getTimePicker: function() {
        if (!this.timePicker) {
            if (this.picker instanceof Ext.ux.TimePicker) {
                this.timePicker = this.picker;
            } else {
                this.timePicker = new Ext.ux.TimePicker(Ext.apply(this.picker || {
					minuteScale: this.minuteScale,
					hourFrom: this.hourFrom,
					hourTo: this.hourTo
				}));
            }

            this.timePicker.setValue(this.value || null);

            this.timePicker.on({
                scope : this,
                change: this.onPickerChange,
                hide  : this.onPickerHide
            });
        }

        return this.timePicker;
    },

    /**
     * @private
     * Listener to the tap event of the mask element. Shows the internal {@link #timePicker} component when the button has been tapped.
     */
    onMaskTap: function() {
        if (Ext.ux.form.TimePicker.superclass.onMaskTap.apply(this, arguments) !== true) {
            return false;
        }

        this.getTimePicker().show();
    },

    /**
     * Called when the picker changes its value
     * @param {Ext.ux.TimePicker} picker The time picker
     * @param {Object} value The new value from the time picker
     * @private
     */
    onPickerChange : function(picker, value) {
        this.setValue(value);
        this.fireEvent('change', this, this.getValue());
    },

    /**
     * Destroys the picker when it is hidden, if
     * {@link Ext.ux.form.icker#destroyPickerOnHide destroyPickerOnHide} is set to true
     * @private
     */
    onPickerHide: function() {
        if (this.destroyPickerOnHide && this.timePicker) {
            this.timePicker.destroy();
        }
    },

    // inherit docs
    setValue: function(value, animated) {
        if (this.timePicker) {
            this.timePicker.setValue(value, animated);
            this.value = (value != null) ? this.timePicker.getValue() : null;
        } else {
            if (Ext.isObject(value)) {
				var hour = value.hour+"";
				hour = hour.length == 1 ? 0 + hour : hour;
				var minute = value.minute+"";
				minute = minute.length == 1 ? 0 + minute : minute;
				this.value =  hour+":"+minute;
            } else {
                this.value = value;
            }
        }

        if (this.rendered) {
            this.fieldEl.dom.value = this.getValue(true);
        }

        return this;
    },

	/**
     * Returns the value of the field, which will be a {@link Time} unless the format parameter is true.
     * @param {Boolean} format True to format the value with Ext.util.Format.defaultTimeFormat
     */
    getValue: function(format) {
        var value = this.value || null;

		if (Ext.isObject(value)) {
			var hour = value.hour+"";
			hour = hour.length == 1 ? 0 + hour : hour;
			var minute = value.minute+"";
			minute = minute.length == 1 ? 0 + minute : minute;
			return hour+":"+minute;
		}
		return value;
    },

    // @private
    onDestroy: function() {
        if (this.timePicker) {
            this.timePicker.destroy();
        }

        Ext.ux.form.TimePicker.superclass.onDestroy.call(this);
    }
});

Ext.reg('timepickerfield', Ext.ux.form.TimePicker);

Ext.ux.Form.TimePicker

Ext.ns('Ext.ux');

/**
 * @class Ext.ux.TimePicker
 * @extends Ext.Picker
 *
 *

A time picker component which shows a TimePicker on the screen. This class extends from {@link Ext.Picker} and {@link Ext.Sheet} so it is a popup.

 *

This component has no required properties.

 *
 *

<h2>Useful Properties</h2>

*

<ul class="list">
 *
	<li>{@link #hourFrom}</li>

*

	<li>{@link #hourTo}</li>

*
</ul>

*
 *

<h2>Example code:</h2>

*
 *

<pre><code>
var timePicker = new Ext.ux.TimePicker();
timePicker.show();
 * </code></pre>
*
*

you may want to adjust the {@link #hourFrom}, {@link #hourTo} and {@link #minuteScale} properties:
*
<pre><code>
var timePicker = new Ext.ux.TimePicker({
    hourFrom: 8,
    hourTo  : 18,
	minuteScale: 5
});
timePicker.show();
 * </code></pre>
*
* @constructor
* Create a new Timepicker
* @param {Object} config The config object
* @xtype timepicker
*/
Ext.ux.TimePicker = Ext.extend(Ext.Picker, {
/**
* @cfg {Number} minuteScale
* List every how many minutes, eg. 5 lists 0, 5, 10, 15, etc. Defaults to 1
*/

minuteScale: 1,

/**
* @cfg {Number} hourFrom
* The start hour for the time picker.  Defaults to 0
*/
hourFrom: 0,

/**
* @cfg {Number} hourTo
* The last hour for the time picker.  Defaults to 23
*/
hourTo: 23,

/**
* @cfg {String} hourText
* The label to show for the hour column. Defaults to 'Hour'.
*/
hourText: 'Hour',

/**
* @cfg {String} minuteText
* The label to show for the minute column. Defaults to 'Minute'.
*/
minuteText: 'Minute',

/**
* @cfg {Array} slotOrder
* An array of strings that specifies the order of the slots. Defaults to <tt>['hour', 'minute']</tt>.
*/
slotOrder: ['hour', 'minute'],

initComponent: function() {
var hoursFrom = this.hourFrom,
hoursTo = this.hourTo,
hours = [],
minutes = [],
ln, tmp, i, j;

// swap values if user mixes them up.
if (hoursFrom &gt; hoursTo) {
tmp = hoursFrom;
hoursFrom = hoursTo;
hoursTo = tmp;
}

for (i = j = hoursFrom; i &lt;= hoursTo; i++, j++) { 			j = (j+"").length &gt; 1 ? j : "0"+j;
hours.push({
text: j,
value: i
});
}

for (i = j = 0; i &lt;= 59; i = j = i + this.minuteScale) { 			j = (j+"").length &gt; 1 ? j : "0"+j;
minutes.push({
text: j,
value: i
});
}

this.slots = [];

this.slotOrder.forEach(function(item){
this.slots.push(this.createSlot(item, hours, minutes ));
}, this);

Ext.ux.TimePicker.superclass.initComponent.call(this);
},

afterRender: function() {
Ext.ux.TimePicker.superclass.afterRender.apply(this, arguments);

this.setValue(this.value);
},

createSlot: function(name, hours, minutes ){
switch (name) {
case 'hour':
return {
name: name,
align: 'right',
data: hours,
title: this.useTitles ? this.hourText : false,
flex: 5
};
case 'minute':
return {
name: name,
align: 'left',
data: minutes,
title: this.useTitles ? this.minuteText : false,
flex: 5
};
}
},

// @private
onSlotPick: function(slot, value) {

Ext.ux.TimePicker.superclass.onSlotPick.apply(this, arguments);
},

/**
* Gets the current value as a Time object
* @return {hour: x, minute: y} value
*/
getValue: function() {
var value = Ext.ux.TimePicker.superclass.getValue.call(this);

return value;
},

/**
* Sets the values of the TimePicker's slots
* @param {Date/Object} value The value either in a {hour:'value', minute:'value'} format or a String, eg: '18:00'
* @param {Boolean} animated True for animation while setting the values
* @return {Ext.DatePicker} this This DatePicker
*/
setValue: function(value, animated) {
if (Ext.isObject(value)) {
this.value = value;
} else {
var arr = (value+"").split(':');
this.value = {
hour: parseInt(arr[0],10),
minute: parseInt(arr[1],10)
};
}

return Ext.ux.TimePicker.superclass.setValue.call(this, this.value, animated);
}
});

Ext.reg('timepicker', Ext.ux.TimePicker);
 

26 Responses to Sencha Touch TimePicker form field and Picker sheet

  1. Mac says:

    Hey Rob, Thanks a lot for this ! saved me plenty of time and works great!

  2. Jakob says:

    The picker component does not seem to work, so I had to inherit from Ext.Sheet instead. Now I can’t seem to get a default value inserted into the picker. I can set the default values (hour and minute), but for some reason the picker values are set to 00:00 instead of the default values I set.

    I would be really happy if you could provide me with a solution to this problem, or point me to where (in the code) the default value is set inside the picker.

    • Rob Boerman says:

      Hi Jacob,

      I will be happy to look into it, first off, which version of Sencha Touch are you using? This picker is based on 1.1

      • Jakob says:

        I’m using Sencha Touch 1.1.0, so that shouldn’t be a problem. Also, what I’ve noticed is that the picker seems to get the correct time after I’ve picked a time once, but it does not get the correct time if I’ve set a default time.

        I’m aware that it could be my fault as I’ve done some changes in order to make the picker work at all, but I still need to know where to find the code where the picker gets/sets its default values

        • Jakob says:

          If you don’t check the comments very often it would be great if you could email me instead. My email address is jakob.grannas@intelliplan.se. I need to fix this asap, so it would really help you could reply as soon as you read this.

          Thank you very much

  3. ChrisW says:

    Rob – what is the best way to integrate these classes into Sencha Touch? I tried dropping into src folder, but figured that sencha-touch.js needs to reference this new components somehow. Or should you just directly include – ?

    • Rob Boerman says:

      Hi Chris,

      In my production environment I concatenate all Sencha sources and my own custom sources using Sencha build and some custom perl scripts. For my development environment everything is just included into the index.html file in this order:
      - sencha-touch.js
      - UX classes (including my own, this also includes the time picker)
      - app.js
      - utils
      - models
      - stores
      - views
      - controllers

      I built a perl script that scans my sources and automatically creates the index.html file for me (among other things). If you like I can send you an example of this index.html file. Hope this helps.

  4. Jakob says:

    Hi Robb,

    I have still not figured out how to get the make the picker set its values to my default values. I’ve console.log:ged this.value practically everywhere in the code, and it always seems to be correct, but the picker still has 00:00 set upon instantiation. I really need your help, and would really appreciate if you could answer my questions.

  5. smantscheff says:

    Thanks a lot. Saved me a lot of work. Works out of the box.

  6. Ryan says:

    I have ported this to Sencha 2.0. After I wasn’t able to deploy this to a Sencha 2.0 app, I created a time picker in the same manner by copying and modifying the existing Sencha 2.0 date picker (control and picker).

    I’ll post it up at some stage.

  7. Divya says:

    Hi,
    Has anyone ported in Sencha touch 2.0. If yes, can you please share the code.

    Thanks,

    • Rob Boerman says:

      No, not yet, Ryan mentioned he was working on it. Otherwise I will port it over in a few days. Have been incredibly busy but should be easy enough to do

  8. Steve c says:

    OK, so I put TimePicker.js in my project in the same directory, register it in index.html, then go to a panel and add an item with an xtype of ‘timepickerfield’, and it doesn’t recognise it. What else do I have to do?

  9. Cem says:

    Hi, thanks for sharing this, however I have got a little problem, I put these 2 files into ux folder under /lib/touch folder and reference it in my index.html just under sencha-touch.js.

    When i added it into my view as below;

    xtype: ‘timepickerfield’,
    name: ‘pickuptime’,
    label: ‘Pick up Time’,
    minuteScale: 5,
    hourFrom: 0,
    hourTo: 23,
    value: {
    hour: (new Date().getHours() + 1) % 24, /* start with the next hour */
    minute: 0
    }

    it throws an error saying Uncaught Error: [Ext.createByAlias] Cannot create an instance of unrecognized alias: widget.timepickerfield

    I use sencha touch 2

    Thanks for help.

    • Rob Boerman says:

      Hi Cem,

      A couple of other people have reported this as well, the current implementation only works in ST 1.
      I will try and free some time to create a similar solution for ST 2 this afternoon.

  10. Cem says:

    Hi there, any progress with ST 2? Im so desperately looking forward to it. Many thanks.

    • Rob Boerman says:

      Hehe, working hard on it. I created a new time picker component for ST2 which works like a charm. Now I just have to create a form time picker field. I’ll create a new blog post and link to it when I am ready. Have patience my friend :)

  11. Arbi says:

    Guys, this is one of working solution:
    http://market.sencha.com/addon/datetimepicker

  12. Hey Rob, This is great. Works smooth and very useful. Thank you very much for sharing

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>