I've gotten the TimeTextBox to work, sort of, in a grid by including a dojo.declare in the script section of my JSP:
dojo.declare(
"dojox.grid.editors.TimeTextBox", dijit.form.editors.Dijit,
{ ..
But, I can't get the result to format properly. I keep getting a full date/time like: Sat Jan 01 1972 14:00:00 GMT-0500 (Eastern Standard Time) instead of just "HH:MM A".
I've tried
dojo.declare("dojox.grid.editors.TimeTextBox", dijit.form.editors.Dijit,{
editorClass: "dijit.form.TimeTextBox",
});
to
dojo.declare("dojox.grid.editors.TimeTextBox", dojox.grid.editors.Dijit, {
editorClass: "dijit.form.TimeTextBox",
// getEditorProps: function(inDatum) {
setValue: function(){
var curDateTime = new Date()
var curHour = curDateTime.getHours()
var curMin = curDateTime.getMinutes()
var curSec = curDateTime.getSeconds()
var curAMPM = " A"
var curTime = ""
if (curHour >= 12){
curHour -= 12
curAMPM = " P"
}
if (curHour == 0) curHour = 12
curTime = curHour + ":"
+ ((curMin < 10) ? "0" : "") + curMin + " " + curAMPM;
return curTime.getValue;
}
});
all the way through to
dojo.declare(
"dojox.grid.editors.TimeTextBox", dijit.form.editors.Dijit,
{
// summary:
// A validating, serializable, range-bound date text box.
// constraints object: min, max
regExpGen: dojo.date.locale.regexp,
compare: dojo.date.compare,
format: function(/*Date*/ value, /*Object*/ constraints){
if(!value || value.toString() == this._invalid){ return null; }
return dojo.date.locale.format(value, constraints);
},
parse: dojo.date.locale.parse,
serialize: dojo.date.stamp.toISOString,
value: new Date(""), // NaN
_invalid: (new Date("")).toString(), // NaN
_popupClass: "dijit._TimePicker",
postMixInProperties: function(){
//dijit.form.RangeBoundTextBox.prototype.postMixInProperties.apply(this, arguments);
this.inherited("postMixInProperties",arguments);
var constraints = this.constraints;
constraints.selector = 'time';
if(typeof constraints.min == "string"){ constraints.min = dojo.date.stamp.fromISOString(constraints.min); }
if(typeof constraints.max == "string"){ constraints.max = dojo.date.stamp.fromISOString(constraints.max); }
},
_onFocus: function(/*Event*/ evt){
// summary: open the TimePicker popup
this._open();
},
setValue: function(/*Date*/ value, /*Boolean, optional*/ priorityChange){
// summary:
// Sets the date on this textbox
this.inherited('setValue', arguments);
if(this._picker){
// #3948: fix blank date on popup only
if(!value || value.toString() == this._invalid){value=new Date();}
this._picker.setValue(value);
}
},
_open: function(){
// summary:
// opens the TimePicker, and sets the onValueSelected value
if(this.disabled){return;}
var self = this;
if(!this._picker){
var popupProto=dojo.getObject(this._popupClass, false);
this._picker = new popupProto({
onValueSelected: function(value){
self.focus(); // focus the textbox before the popup closes to avoid reopening the popup
setTimeout(dojo.hitch(self, "_close"), 1); // allow focus time to take
// this will cause InlineEditBox and other handlers to do stuff so make sure it's last
dijit.form.TimeTextBox.superclass.setValue.call(self, value, true);
},
lang: this.lang,
constraints:this.constraints,
isDisabledDate: function(/*Date*/ date){
// summary:
// disables dates outside of the min/max of the TimeTextBox
return self.constraints && (dojo.date.compare(self.constraints.min,date) > 0 || dojo.date.compare(self.constraints.max,date) < 0);
}
});
this._picker.setValue(this.getValue() || new Date());
}
if(!this._opened){
dijit.popup.open({
parent: this,
popup: this._picker,
around: this.domNode,
onCancel: dojo.hitch(this, this._close),
onClose: function(){ self._opened=false; }
});
this._opened=true;
}
dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth });
},
_close: function(){
if(this._opened){
dijit.popup.close(this._picker);
this._opened=false;
}
},
_onBlur: function(){
// summary: called magically when focus has shifted away from this widget and it's dropdown
this._close();
this.inherited('_onBlur', arguments);
// don't focus on input. the user has explicitly focused on something else.
},
getDisplayedValue:function(){
return this.textbox.value;
},
setDisplayedValue:function(/*String*/ value){
this.textbox.value=value;
}
}
);
All three give me the TimeTextBox but the result always comes out as the fully qualified date/time ...

I found...
that when declaring "dojox.grid.editors.TimeTextBox" as you did above, any console.log statements I issued inside of the "setValue" function were ignored. I included the "getValue" function in the declaration, and they were being called whenever I entered data. So, I think if move your test code in "setValue" to "getValue" instead, you might get a more desirable result. However, I found that if I just created a formatter for the column, it was much easier.
This is all the code I needed in the declaration:
editorClass: "dijit.form.TimeTextBox"
});
Here's an example of my grid:
dojoType="dojox.Grid"
jsId="grid"
singleClickEdit="true"
model="model"
structure="grid_structure"
style="width: 800px;
height: 400px">
<script type="dojo/method">
grid_structure = [
{ type: "dojox.GridRowView", width: "20px" },
{ cells: [[
{ name: 'First Name', field: 'first_name'
,editor:dojox.grid.editors.Dijit }
,{ name: 'Last Name', field: 'last_name', editor:dojox.grid.editors.Dijit }
,{ name: 'Time', field: 'time'
,editor: dojox.grid.editors.TimeTextBox, formatter: formatTime }
]]}
];
grid.setStructure(grid_structure);
</script>
</div>
And here's the formatter function:
if(inDatum != dojox.grid.na &&
typeof inDatum == "object"){
var hrs = inDatum.getHours();
var mins = inDatum.getMinutes();
var pd = (hrs >= 12) ? " PM": " AM";
if(hrs >= 12){
hrs -= 12;
}
else if(hrs == 0){
hrs = 12;
}
if(mins == 0){
mins = "00";
}
var time = hrs + ":" + mins + pd;
return time;
}
return inDatum;
}
It will display something like "12:00 PM", but if you reference the value of the cell (e.g. grid.model.data[0].time), it's still the date object (i.e. Thu Jan 01 1970 10:00:00 GMT-0500 (EST)).
When pulling date values
When pulling date values from a store (i.e. strings of the format "10:00 AM"), the TimeTextBox was throwing errors (since it's expecting date objects), so I had to tweak the declaration a bit more:
editorClass: "dijit.form.TimeTextBox"
//called first time the cell in a particular column is clicked
,getEditorProps: function(inDatum){
//if inValue is coming from the store, it's a string
if(typeof inDatum == "string"){
//if it's a valid time string, make it a date object
if(inDatum.indexOf(":") >= 0){
var val = timeStrToObj(inDatum);
}
//if not, make it empty (or ItemFileWriteStore throws an assertion error)
else{
var val = "";
}
}
//if not from store, it was an object created by TimeTextBox
else if(typeof inDatum == "object"){
var val = inDatum;
}
return dojo.mixin(this.inherited(arguments), {
value: val
});
}
//called in place of getEditorProps for every cell click after initial
,setValue: function(inRowIndex, inValue){
if(this.editor){
//if inValue is coming from the store, it's a string
if(typeof inValue == "string"){
//if it's a valid time string, make it a date object
if(inValue.indexOf(":") >= 0){
var val = timeStrToObj(inValue);
}
//if not, make it empty (or ItemFileWriteStore throws an assertion error)
else{
val = "";
}
}
//if not from store, it was an object created by TimeTextBox
else if(typeof inValue == "object"){
var val = inValue;
}
this.editor.setValue(val);
}
else{
this.inherited(arguments);
}
}
//called when cell value is applied
,getValue: function(inRowIndex){
//if no value is entered and the cell is applied, an assertion error is thrown
var val = (typeof this.editor.getValue() != "undefined") ? this.editor.getValue() : "";
return val;
}
});
Here are the helper functions I'm using:
//string that you pass in (format "10:00 AM")
function timeStrToObj(p_time_str){
var time_obj = new Date("January 1, 1970");
var time_arr = parseTimeStr(p_time_str,true);
time_obj.setHours(time_arr["hours"]);
time_obj.setMinutes(time_arr["minutes"]);
time_obj.setSeconds(0);
return time_obj;
}
//@desc: parses a time string of the format "HH:MM PD" into
//an associative array with keys "hours", "minutes", and "period"
function parseTimeStr(p_time_str,p_military_bool){
var time_arr = p_time_str.split(":");
var hrs = parseInt(time_arr[0]);
time_arr = time_arr[1].split(" ");
var mins = time_arr[0];
var pd = time_arr[1];
time_arr = [];
if(typeof p_military_bool != "undefined" &&
p_military_bool == true){
if(pd == "PM" && hrs != 12){
hrs += 12;
}
else if(pd == "AM" && hrs == 12){
hrs = 0;
}
}
time_arr["hours"] = hrs.toString();
time_arr["minutes"] = mins;
time_arr["period"] = pd;
return time_arr;
}
//@desc: pass in a date object and return a
//time string of the format "HH:MM PD"
function timeObjToStr(p_time_obj){
var hrs = p_time_obj.getHours();
var mins = p_time_obj.getMinutes();
var pd = (hrs >= 12) ? " PM": " AM";
if(hrs > 12){
hrs -= 12;
}
else if(hrs == 0){
hrs = 12;
}
if(mins == 0){
mins = "00";
}
var time_str = hrs + ":" + mins + pd;
return time_str;
}
If you want to round the
If you want to round the values that users type in (as opposed to selecting from the list) to the nearest quarter (the same way the TimeTextBox does), replace "getValue" above with this one:
//if no value is entered and the cell is applied, an assertion error is thrown
var val = (typeof this.editor.getValue() != "undefined") ? this.editor.getValue() : "";
//round minutes to the nearest quarter
if(typeof val == "object"){
val = roundMinutes(val);
}
return val;
}
You'll also need this helper function:
function roundMinutes(p_date_obj){
var mins = p_date_obj.getMinutes();
var diff;
//if the minutes are in quarters, just return;
//otherwise, round them to the appropriate quarter
if(mins != 0 && mins != 15 &&
mins != 30 && mins != 45){
if(mins > 45){
diff = (mins <= 52) ? (45 - mins) : (60 - mins);
}
else if(mins > 30){
diff = (mins <= 37) ? (30 - mins) : (45 - mins);
}
else if(mins > 15){
diff = (mins <= 22) ? (15 - mins) : (30 - mins);
}
else{
diff = (mins <= 7) ? (0 - mins) : (15 - mins);
}
}
else{
diff = 0;
}
//add the difference; the date object will automatically round the hours
p_date_obj.setMinutes(p_date_obj.getMinutes() + diff);
return p_date_obj;
}
your response on my dilemma of TimeTextBox in Grid
Thank you so much, uwf_doc! Your comprehensive response should allow us to take our editing needs from dialogs to the grid, which was our initial goal!