PDA

View Full Version : [new version] DateTime Field



Pages : 1 2 [3] 4

haloween
17 May 2009, 10:25 PM
Hi !

I've wanted to automatically set date to now() on widget render.


{items: {xtype: 'xdatetime',name: 'kiedy',labelWidth: 30,fieldLabel: 'Kiedy',increment: 5,timeFormat:'H:i',timeConfig : {minValue: '7:00',maxValue: '21:00',increment: 30}}}So onRender of the whole element i executed


this.findByType('xdatetime')[0].setValue(new Date())cool , the date was set.But if user would like to alter the date , or time the values in widget are changing but they're not reflected in the sent date.....

ex.

the date is auto set to : 2009-05-18 08:27 , i set the time to 09:00.

But the date sent to the server is 08:27 .... (:|

bbirtle
27 May 2009, 3:47 AM
Saki,
Thanks very much for some very cool components! Your DateTime one here seems to work great and saved me a lot of effort.

I did notice that if you use the renderTo config option, then an error is produced when it tries to add an icon. This can be solved by adding class="x-form-element" to the target div.

Thanks again!
- Brian

jsakalos
27 May 2009, 8:25 AM
Can you post a simple showcase?

Opsone
4 Jun 2009, 6:08 AM
Hi everyone,

We figure out some problems with loading date object from store into this component with IE7.

We load a date Object and submit the form and the result is NaN-NaN-NaN NaN:NaN:NaN.

IE7 doesn't like new Date(date) where date is already a date object.

Our modified setValue function:



,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat);
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da, time;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
//this.dateValue = new Date(val); // IE7 LOADING PROBLEM
this.dateValue = new Date(val.getTime()); // E7 LOADING PATCH
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}

this.updateValue();
} // eo function setValue

jsakalos
4 Jun 2009, 10:50 AM
Enclose it in if(Ext.isIE....) block and I can include it in the core code.

Opsone
4 Jun 2009, 11:24 AM
Enclose it in if(Ext.isIE....) block and I can include it in the core code.



,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat);
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da, time;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}

this.updateValue();
} // eo function setValue


Sound's good ? ;)

jsakalos
4 Jun 2009, 11:28 AM
Looks fine - will include it.

einavb
8 Jun 2009, 12:00 AM
Saki, great plugin !
came across a possible small quirk: when viewed in IE7/Win XP the right icon offest 1px from the bottom.
I am using it in grid top toolbar like so:


var gridFilterBar = new Ext.Toolbar({
items: [
'Filter:',
{xtype: 'tbspacer'},
{xtype: 'tbspacer'},
'<b>From Date:</b>',
{xtype: 'tbspacer'},
{
name: 'startdt',
id: 'startdt',
format: 'd-m-Y',
xtype: 'datefield',
endDateField: 'enddt'
},
{xtype: 'tbspacer'}, {xtype: 'tbspacer'},
{xtype: 'tbspacer'}, {xtype: 'tbspacer'},
'<b>To Date:</b>',
{xtype: 'tbspacer'}, {xtype: 'tbspacer'},
{
name: 'enddt',
id: 'enddt',
format: 'd-m-Y',
xtype: 'xdatetime',
timePosition: 'right',
startDateField: 'startdt'
}]
});


when displayed inside a form (like the example used inside your code), it is aligned just fine.

Thanks,
Einav.

http://img14.imageshack.us/img14/3171/offsetv.th.jpg (http://img14.imageshack.us/img14/3171/offsetv.jpg)

abraxxa
8 Jun 2009, 1:15 AM
Should the extension work with ExtJS 3.0 too?
I still have rendering issues with 3.0 rc2 when using it in a hidden panel's top toolbar which can be expanded by the user.

jsakalos
8 Jun 2009, 10:42 AM
@einavb, some css IE specific tweak(s) may be necessary. I'm on Linux and FF so it's hard to test and debug IE problems for me. Can you try?

jsakalos
8 Jun 2009, 10:45 AM
@abraxxa, it looks like it does work, however, I've tested only briefly. Rendering anything into a hidden panel will always be problem. Anyway, I could take a look if you posted a showcase.

einavb
10 Jun 2009, 12:26 AM
@einavb, some css IE specific tweak(s) may be necessary. I'm on Linux and FF so it's hard to test and debug IE problems for me. Can you try?

Saki,
I guess it needs further testing, but removing IE hack in line 173:

// workaround for IE trigger misalignment bug
if(Ext.isIE && Ext.isStrict) {
t.select('input').applyStyles({top:0});
}fixed it for me..

Thank's,
Einav.

jsakalos
10 Jun 2009, 2:02 AM
Perfect, thank you.

abraxxa
10 Jun 2009, 3:24 AM
That's an excerpt of my code. nothing fancy, just a toolbar used as top-toolbar of a panel:



var tb_graphs = new Ext.Toolbar({
items:[
'limit [kbit/s]:', {
xtype:'numberfield',
id:'field_limit',
allowBlank:true,
allowDecimals:false,
allowNegative:false,
emptyText:'dynamic',
maxLength:8,
name:'limit',
width:100
},
'-',
'',
'start:', {
xtype:'xdatetime',
id:'field_startdate',
name:'datetime_start',
dateFormat:Date.patterns.Date,
dateConfig:{
allowBlank:false,
emptyText:'YYYY-MM-DD'
},
timeFormat:Date.patterns.TimeShort,
timeConfig:{
allowBlank:false,
emptyText:'HH:MM',
increment:60
},
// default is yesterday
value:new Date().add(Date.DAY, -1)
},
'-',
{
text:'refresh',
handler:updateGraph
},
'->'
]
});

var panel_interface = new Ext.Panel({
title:'Interface [% interface.portname %]',
iconCls:'icon-interface',
collapsible:false,
tbar:tb_interface,
items:[
],
renderTo:'main'
});

anotherpit
10 Jun 2009, 6:00 AM
@jsakalos
Current implementations of setVisible(), show() and hide() seem to break basic Ext.Component's approach. I'd suggest you the following code:

,onShow:function() {
this.df.show();
this.tf.show();
return Ext.ux.form.DateTime.superclass.onShow.call(this);
}
,onHide:function() {
this.df.hide();
this.tf.hide();
return Ext.ux.form.DateTime.superclass.onHide.call(this);
}
instead of

,setVisible: function(visible){
if(visible) {
this.df.show();
this.tf.show();
}else{
this.df.hide();
this.tf.hide();
}
return this;
}
,show:function() {
return this.setVisible(true);
}
,hide:function() {
return this.setVisible(false);
}

jsakalos
10 Jun 2009, 8:13 AM
What is the problem? Which version of Ext are you talking about?

anotherpit
10 Jun 2009, 3:11 PM
@jsakalos
Weak compatibility with standard behaviour is a problem itself, isn't it? Besides other issues, current implementation causes bug in MSIE and Opera when you try to hide DateTime field: internal date and time fields become hidden while the wrapping element does not. Both in Ext 2.x and 3.x.

PremiereGlobal
11 Jun 2009, 11:24 AM
we are using the Ext.ux.form.DateTime .
We want to show the date range on the calender and disable other dates.
How can we set the setMaxValue and setMinvalue on the dates so that other dates are disable on the calender?

thanks

jsakalos
11 Jun 2009, 11:55 AM
DateTime exposes df and tf properties (not documented yet) that are references to the underlying Date and Time Fields. You can use df to call its all methods of DateField.

PremiereGlobal
12 Jun 2009, 8:03 AM
Thanks Saki,

I am trying to set Min and Max value on the tf but getting the following error:-
this.tf.setMinValue is not a function
[Break on this error] this.tf.setMinValue(date);



Are we missing something on this

jsakalos
12 Jun 2009, 12:29 PM
What "this" points to? Also, tf stands for TimeField and that doesn't have setMinValue method. df stands for DateField.

PremiereGlobal
12 Jun 2009, 12:47 PM
We are creating the item(Ext.ux.form.DateTime) and on reset button we need to set the minValue for TimeField.
while creating Ext.ux.form.DateTime,we are setting the TimeField with min and max value :-

items:new Ext.ux.form.DateTime({
id:'itemlevelfromDateFieldId',
hiddenName:'strFromDate',
name:'strFromDate',
readOnly: true,
timeConfig:{
minValue:this.minTimeFieldValue,
maxValue:this.maxTimeFieldValue,
increment:5
}
})

But how can i change the minValue and maxValue for TimeField after the Ext.ux.form.DateTime is loaded .
so we are trying to use tf.setMinValue(date) function but does work.

Is there any way we can reset the min and max value for timefield.

jsakalos
12 Jun 2009, 12:50 PM
You'd need to re-create records in TimeField's underlying store plus set tf.minValue = yourMinValue.

See TimeField::initComponent

Hani79
26 Jun 2009, 12:23 PM
I'm hitting a bug, and I am not sure if the culprit is the browser or the JS.

If the date field is focused and I click the dropdown on the time field, the datetime field blurs rather than dropping down the available times to select from. It also happens in the opposite direction. If the time field is focused, and I click on the datepicker for the date field, the datetime field blurs instead of poping open the datepicker.

This is happening in FF, Opera, and Safari.

However, in IE7 it works correctly (surprisingly).

Any suggestions?

jsakalos
26 Jun 2009, 1:00 PM
I cannot reproduce the behavior you describe at http://examples.extjs.eu/?ex=fs2col

A showcase?

Hani79
26 Jun 2009, 3:07 PM
Yeah, you're right - its not happening in your example.

Here is what's happening with mine: http://www.mmbarn.com/test/

The link to the JS is at the top of the page.

(I hope and pray that its not something stupid I did...otherwise I'll feel like an .) :)

jsakalos
26 Jun 2009, 3:19 PM
Ext version can be different. Also, I'd suggest vertical arrangement like here: http://recordform.extjs.eu/

Hani79
30 Jun 2009, 1:17 PM
Thanks for the feedback, Saki. I verified that I was using the latest version 2.2.1 and implemented the vertical arrangement for the fields.

However, I'm still seeing the problem.

I actually went to the new link you provided (http://recordform.extjs.eu/) and saw the same problem that I was having. Is that something you can see as well?

If not, it may be something funky on my end. (I'm using FF 3.0.1, not FF3.5.) However, I'll update to 3.5 right now and see what happens. I'll add a post in a few minutes.

jsakalos
30 Jun 2009, 1:44 PM
Believe me or not, I really cannot reproduce it - now on FF-3.5@Linux. Try from another computer/browser if it is not a conflicting add-on.

My attempt to reproduce:
1. Set focus to the date part of DateTime
2. Click time part trigger
3. Dropdown appears as expected

Hani79
30 Jun 2009, 2:06 PM
I really do believe you. :)

I am stumped, though, that I am still able to reproduce the issue.

I've updated my computer to FF 3.5 (Vista) and I've tried it on my wife's computer (FF 3.5 on XP) and can recreate it in my test area as well as on http://recordform.extjs.eu/ .

I do have to say, that I have seen it work fine maybe once or twice - and I don't know why.

The steps to recreate the problem that I am using at http://recordform.extjs.eu/:

1. Double click on one of the dates.
2. Click in the date field (say, in the middle of the date)
3. Click on the trigger for the time field
4. The edit mode closes (instead of the time options dropping down)

I really do appreciate you bearing with me! I know you haven't been able to recreate the problem I'm having, so that can be as frustrating as the problem itself. :)

jsakalos
30 Jun 2009, 2:22 PM
I did the above steps, still w/o seeing any problem. I've tried all browsers I have on Linux (FF, Seamonkey, Opera, Chrome-beta) and no problem.

Then I went to Window$ and tested again (FF, Opera, IE7, Chrome, Safari) and no problem again.

It could be:

- an old cached version at your end
- windows difference - I tested with XP en-US version
- something else we both don't understand so that we cannot find yet ;)

Hani79
30 Jun 2009, 3:55 PM
Wow - you've done extensive testing. Thanks for all your efforts to help me!

I cleared out my cache and still see the issue.

I went ahead and implemented another datetime field (outside of the editor grid) on my testing page and it seems to work just fine. Maybe it has something to do with the fact that the datetime field is in an editor grid? After all, I experience the problem in your editor grid example also - but not in your fieldsets example.

I am also using the en-US version of XP and Vista.

Another thing: I launched Safari and went to my test site. The first time I tried it, it worked just fine. Everytime I tried it aftewards, it didn't. That doesn't give me any clues, but it may give you an idea?

Would it help at all if I did a video screen capture to show you exactly what's happening?

jsakalos
30 Jun 2009, 4:16 PM
No idea. DateTime is used in my application in a grid. The application is used by ~100 companies ranging from ~2 to ~70 employees that login. They would discover it already.

It is a complete mystery to me.

Just to be sure, I append the version I use - should be same as download devel version.

quicksilver_in
1 Jul 2009, 7:09 PM
Hi Saki,

I am using ext 3.0 The RowActions which are compatible now with Ext 3 are being used to get the recordForm which is rendering DateTime.

The issue I see is
a) I can see the correct rendering of the date\time field in the grid. No issues there but when I click on the edit action, the recordForm is rendering only the datefield and not the time field. I have all the latest files from the rowAction devel. version.

b) Once I have selected the date in recordForm ( the pop up from edit action), the format is shown as "2009-07-02IST00:00:00"

Not sure what's going wrong here. Attached is the image.

So far I have debugged it a little bit.

I debugged the issue using frebug and found out the following differences 2.2 vs 3.0. Ext 2.2 generated code which is working fine is as follows. Plz. note how everthing s nicely put in table body.


<div id="ext-gen950" class="x-form-field-wrap">
<table id="ext-gen949" style="border-collapse: collapse;">
<tbody>
<tr>
<td id="ext-gen952" style="padding-bottom: 1px;" class="ux-datetime-date">
<div style="width: 118px;" id="ext-gen953" class="x-form-field-wrap">
<input style="width: 101px;" class="x-form-text x-form-field" size="10" autocomplete="off" id="ext-comp-1086-date" type="text">
<img id="ext-gen954" src="http://localhost/intra/resources/ext-2.2/resources/images/default/s.gif" class="x-form-trigger x-form-date-trigger">
</div></td></tr><tr><td id="ext-gen963" class="ux-datetime-time"><div style="width: 118px;" id="ext-gen964" class="x-form-field-wrap">
<input style="width: 101px;" class="x-form-text x-form-field" size="24" autocomplete="off" id="ext-comp-1086-time" type="text">
<img id="ext-gen965" src="http://localhost/intra/resources/ext-2.2/resources/images/default/s.gif" class="x-form-trigger x-form-arrow-trigger">
</div></td></tr></tbody></table>

Below code is what is generated in Ext 3.0. Table is missing. This would essentially mean the logic of putting in the html tags in dataTime.js gets messed up


<div id="x-form-el-ext-comp-1117" class="x-form-element" style="padding-left: 85px;">
<div id="ext-gen392" class="x-form-field-wrap x-form-field-trigger-wrap x-trigger-wrap-focus" style="width: 118px;">
<input id="ext-comp-1117" class="x-form-text x-form-field x-form-focus" type="text" name="purchaseDate" autocomplete="off" size="10" tabindex="5" style="width: 101px;"/>
<img id="ext-gen393" class="x-form-trigger x-form-date-trigger" src="http://localhost/intra/ext-3.0/resources/images/default/s.gif"/>
</div>
</div>
<div class="x-form-clear-left"/>
</div>
</div>
</div>
</div>
<div id="ext-comp-1118" class="x-panel x-panel-noborder x-form-label-left x-column" style="width: 228px;">
</div>
<div id="ext-gen389" class="x-clear"/>
</div>

jsakalos
1 Jul 2009, 11:21 PM
Yes, the generated html may have changed between 2 and 3 so new rendering can be necessary for Ext 3.

quicksilver_in
1 Jul 2009, 11:43 PM
Yes, the generated html may have changed between 2 and 3 so new rendering can be necessary for Ext 3.

Saki,

Are you planning to update this for Ext 3.0 considering there are people who are looking forward to use this. I read somewhere in this thread itself that about 100 companies are using this without any complaints. Probably, they would be looking for similar stuff once they decide to upgrade.

I looked at dateTime code and found that your rendering is conditional. But, still it has to generate the html for either of the conditions. In both the conditions you are creating children for both dateField and timeField.


var t;
if('below' === this.timePosition || 'bellow' === this.timePosition) {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[{tag:'td', style:'padding-bottom:1px', cls:'ux-datetime-date'}]}
,{tag:'tr',children:[{tag:'td', cls:'ux-datetime-time'}]}
]}, true);
}
else {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[
{tag:'td',style:'padding-right:4px', cls:'ux-datetime-date'},{tag:'td', cls:'ux-datetime-time'}
]}
]}, true);
}What I don't understand how Ext-3.0 completely ignored the other child ?
Thanks much

jsakalos
2 Jul 2009, 12:01 AM
Yes, but not before Ext 3 is out. I'll need to port the whole app to Ext 3, so I'll do it as a part of this big job.

Hani79
2 Jul 2009, 10:01 PM
Just to be sure, I append the version I use - should be same as download devel version.

Oh my goodness. Either I'm an or these late nights are starting to get to me. Apparently, I decided to not use the latest version (even though I thought I was).

Saki - I wasted your time and I feel terrible. I'm usually not that absent-minded. Sorry! :s

jburnhams
17 Jul 2009, 1:09 AM
Hi Saki, thanks for DateTime, it's very useful. Now Ext3 is out, we're looking into upgrading to that. Do you have any idea when you'll have time to do an Ext3 compatible DateTime?

dizor
17 Jul 2009, 9:09 AM
I think that "style" doesn't work - because it's assign to "input hidden"



xtype:'xdatetime',
fieldLabel:'Test',
style:'margin-bottom:2px'

jsakalos
17 Jul 2009, 11:22 AM
I admit, however, I've never needed a style on a form field.

Sigma
23 Jul 2009, 7:45 AM
Hi Saki,

First of all, thank you for this elegant extension. I am using 2 datetime objects in a formpanel. They render perfectly in FireFox 3, but do not load in IE 7. Has anyone else been having this problem? Here is the code I used to create the objects:
xtype:'xdatetime',
id:'sdtf',
fieldLabel:'Start D&T',
anchor:'-18',
timeFormat:'H:i:s',
dateFormat:'d.n.Y',
//dateConfig: { emptyText: 'Start Date' },
//timeConfig: { emptyText: 'Start Time' }
},{
xtype:'xdatetime',
id:'edtf',
fieldLabel:'End D&T',
anchor:'-18',
timeFormat:'H:i:s',
//timeConfig: { emptyText: 'End Time' },
//dateConfig: { emptyText: 'End Date' },
dateFormat:'d.n.Y'
// more items Thank you for any help you can give.



EDIT: ARGH Why can I never see the problem until after I ask for help about it! Sorry about this, it was just a case of IE getting messed up by trailing commas.

jsakalos
23 Jul 2009, 10:45 AM
Have you seen http://blog.extjs.eu/philosophy/leading-comma-or-trailing-comma-that-is-the-question/ ?

;) ;) ;)

incaic
28 Jul 2009, 12:28 PM
Great work Saki, as always. Thanks!

reset() didn't work for me. Am I missing something? Isn't the following needed?


,reset:function(){
this.df.reset();
this.tf.reset();
} // eo function reset

jsakalos
28 Jul 2009, 12:33 PM
Yes, you're right, added.

incaic
29 Jul 2009, 2:58 PM
Forgot to update the hidden field ... here's the code:



/**
* Calls reset on the DateField and TimeField
* and updates the hidden field
*/
,reset:function(){
this.df.reset();
this.tf.reset();
this.updateTime();
this.updateHidden();
} // eo function reset

rob_howe
5 Aug 2009, 12:02 PM
When upgrading from extjs-2.2.1 to extjs-3.0.0 this DateTime picker extension stopped working for me. Apparently the default format returned from Date() has changed in Ext3.0. Instead of a value in format: 2009-08-05T16:01:02
I was getting: Wed Aug 05 2009 16:01:02 GMT-0400 (Eastern Daylight Time)

So I changed one line of code and everything now works as before:


,getValue:function() {
// create new instance of date
// Replaced the following line of code to cludge this to work on Ext3.0
// return this.dateValue ? new Date(this.dateValue) : '';
return this.dateValue ? new Date(this.dateValue).format(this.hiddenFormat).replace(/ /, 'T') : '';


Note the ".replace(/ /,'T')" isn't necessary, but I wanted the output to look exactly the same as before.

Comments anyone?

tarini
22 Aug 2009, 3:20 PM
great job saki! really!

but I've got a little problem... I would like to use msgTarget = "side" to notify validation error to the user and I forced this configuration for each sub-field in this way:



timeConfig: {
......
msgTarget: 'side'
},
dateConfig: {
....
msgTarget: 'side'
}



but i got the attachment error...

how can I fix it?

a possible solution should be to check if msgTarget == "side" and, if condition is positive, reduce dateField's width leaving a empty space between this and timeField

thanks :)

jsakalos
23 Aug 2009, 3:29 AM
It creates 2 icons, one for time, one for data. I think that there should be only a logic to display only one icon for both fields. There is some logic implemented but it seems to be buggy...

tarini
23 Aug 2009, 3:50 AM
I think it will be cool using a single error icon (maybe the one of the field on the right) to show every error thrown by both field

I get right-positioned field in this way:


var rightField = this.timePosition == 'right' ? this.tf : this.df;


but in this way tooltip over timeField should be "date format error" and it makes no sense
:(

aborjinos
24 Aug 2009, 6:24 AM
is it possible to add this extension to grid filter options:-/

jsakalos
24 Aug 2009, 11:07 PM
I've never tried this combination; you're welcome to test and inform us on the result.

dekely
25 Aug 2009, 3:28 AM
Hi All,

After I upgraded, I cannot open the TIME combobox.

Any idea what to check ?

Is there a new dateTime field for 3.0 ?

thanks
Dekel

tarini
27 Aug 2009, 12:32 PM
just to propose a little fix about validation and errorIcon



// create icon for side invalid errorIcon
if('side' === this.msgTarget) {
var elp = this.el.findParent('.x-form-element', 10, true);
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});

var o = {
errorIcon: this.errorIcon,
msgTarget: "side",
alignErrorIcon: this.alignErrorIcon.createDelegate(this)
}
Ext.apply(this.df, o);
Ext.apply(this.tf, o);
}


Setting some common attributes to this.df and this.tf (errorIcon, msgTarget) and overriding alignErrorIcon method with DateTimeField one, allow me to have a single errorIcon for both fields.

I attached an image to show how field now behaves...
http://img44.imageshack.us/i/screenshot1uda.png/ (forum upload feature doesn't work -_-)

shiv
27 Aug 2009, 12:38 PM
I'm trying to use the DateTime Field with a rowEditor. I'm getting the following two errors when clicking the Time combo:

editor.column is undefined

This refers to these lines in the rowEditor extension:



ensureVisible: function(editor){
if(this.isVisible()){this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);}
},


this.list is undefined

This refers to the Combobox section of ext-all.js.

By the way, is anyone else having trouble posting to this forum?

jsakalos
27 Aug 2009, 9:02 PM
@tarini,

the alignment is fixed by your patch, however, there is sill a little issue when both date and time are invalid. Anyway, it's better than it was.

Thank you.

tarini
28 Aug 2009, 7:11 AM
@tarini,
there is sill a little issue when both date and time are invalid.


you are right... the problem was about subfields clearInvalid methods that will hide errorIcon although other subfield remains invalid

I fix in this way



if('side' === this.msgTarget) {
var elp = this.el.findParent('.x-form-element', 10, true);
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});

var o = {
errorIcon: this.errorIcon,
msgTarget: "side",
alignErrorIcon: this.alignErrorIcon.createDelegate(this),
clearInvalid: function() {
if(!this.rendered || this.preventMark){ // not rendered
return;
}
this.el.removeClass(this.invalidClass);
this.fireEvent('valid', this);
}
}
Ext.apply(this.df, o);
Ext.apply(this.tf, o);
}


for me now is ok now... I'll use this plugin in production environment :)

sprestel
2 Sep 2009, 2:04 AM
hello, very nice Extension.

how i can select a value in the dropdown, if my given time is something like 11:23 and not in the list....

many thanks

jsakalos
3 Sep 2009, 12:44 AM
You cannot. Just type 11:23. List contains only major times configurable in increment option.

advarot
7 Sep 2009, 11:51 PM
Hi,
I just upgraded to Ext 3.0, and I'm getting exception from the DateTime component.
Is there any upgrade to this component?
I tried the version from 2008-06-12 and also the version from 2009-03-04.

I would appreciate any help.
Thanks

nbourdeau
15 Sep 2009, 7:16 AM
Hi, i suggest adding this code to support custom validators of internals fields :

In the default config :


/**
* @cfg {String} A custom validation function to be called during date field validation (defaults to null).
*/
,dateValidator:null

/**
* @cfg {String} A custom validation function to be called during time field validation (defaults to null).
*/
,timeValidator:null


In the initComponent call :

// create DateField
var dateConfig = Ext.apply({}, {
id:this.id + '-date'
,format:this.dateFormat || Ext.form.DateField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
,validator:this.dateValidator
}, this.dateConfig);
this.df = new Ext.form.DateField(dateConfig);
this.df.ownerCt = this;
delete(this.dateFormat);

// create TimeField
var timeConfig = Ext.apply({}, {
id:this.id + '-time'
,format:this.timeFormat || Ext.form.TimeField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
,validator:this.timeValidator
}, this.timeConfig);


just sharing what i've done !

jsakalos
15 Sep 2009, 11:22 AM
Good idea! Thank you very much. I've updated the first post.

Kirill_Sychin
8 Oct 2009, 2:10 AM
I think that destroying work incorrectly. If we init component but doesn't render it, we have a unusing link to object.
This is my version.


beforeDestroy:function() {
if(this.isRendered) {
// this.removeAllListeners();
this.wrap.removeAllListeners();
this.wrap.remove();
this.tableEl.remove();
}
this.df.destroy();
this.tf.destroy();
} // eo function beforeDestroy

jsakalos
8 Oct 2009, 5:37 AM
Could you please elaborate what exactly is the problem with the original beforeDestroy?

Kirill_Sychin
8 Oct 2009, 5:45 AM
I create TabPanel with 2 items: simple form and form with DateTime. Set as active a first tab. DateTime component was initialized (called initComponent for secord form), but was not rendered (tab panel is not active). Then I'm closing (destroying) this TabPanel. Into Ext.ComponentMgr I find DateTime even though he was destroyed.

This is my problem.

jsakalos
8 Oct 2009, 8:50 AM
Is there a markup or instance of the object? If it is instance only just set it to null.

Kirill_Sychin
8 Oct 2009, 10:55 PM
It is instance of DateTime. Where should I insert 'set to null'? Into destroy function of my form?

jsakalos
10 Oct 2009, 9:11 AM
E.g.:


var p = new Ext.ux.form.DateTime(....);

// ....

p.destroy();
p = null;

Rotomaul
12 Oct 2009, 3:06 PM
In case anyone is interested, I modified initComponent() so I can initialize Ext.ux.form.DateTime with listeners (I needed to add a 'select' event to Ext.form.TimeField).



,initComponent:function() {
// call parent initComponent
Ext.ux.form.DateTime.superclass.initComponent.call(this);

// create DateField
var dateConfig = Ext.apply({}, {
id:this.id + '-date'
,format:this.dateFormat || Ext.form.DateField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.dateValidator
// ,listeners:{
// blur:{scope:this, fn:this.onBlur}
// ,focus:{scope:this, fn:this.onFocus}
// }
}, this.dateConfig);
if(!dateConfig.listeners){dateConfig.listeners={};}
Ext.apply(dateConfig.listeners,{ //Add listeners
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
});
this.df = new Ext.form.DateField(dateConfig);
this.df.ownerCt = this;
delete(this.dateFormat);

// create TimeField
var timeConfig = Ext.apply({}, {
id:this.id + '-time'
,format:this.timeFormat || Ext.form.TimeField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.timeValidator
// ,listeners:{
// blur:{scope:this, fn:this.onBlur}
// ,focus:{scope:this, fn:this.onFocus}
// }
}, this.timeConfig);
if(!timeConfig.listeners){timeConfig.listeners={};}
Ext.apply(timeConfig.listeners,{ //Add listeners
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
});
this.tf = new Ext.form.TimeField(timeConfig);
this.tf.ownerCt = this;
delete(this.timeFormat);

// relay events
this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);

} // eo function initComponent

durlabh
15 Oct 2009, 4:44 AM
As per Evan, implementation of grid keyboard navigation has changed. Hence, this extension doesn't work in EditorGrid anymore. See http://www.extjs.com/forum/showthread.php?t=82953 for more details. Anybody has had luck getting it to work with latest SVN release of 2.x branch?

dphu
15 Oct 2009, 6:08 AM
Guys,
Can someone please post a final code for xdatetime field?
Thank much!

durlabh
15 Oct 2009, 6:10 AM
Guys,
Can someone please post a final code for xdatetime field?
Thank much!

As per Saki, he updated the first post with the latest code recently.

dphu
15 Oct 2009, 6:20 AM
But I don't see updates for bug fixes/enhancements from latter posts in Saki's original post, like time validator, etc. Would be nice if we a central place to merge all the latest enhancements/bug fixes. Any ideas? Thx!

durlabh
15 Oct 2009, 6:26 AM
AFAIK, first post does contain the code with validators.

jsakalos
16 Oct 2009, 2:20 AM
First post contains latest version (rev 749).

durlabh
16 Oct 2009, 1:15 PM
For this to get working with latest 2.x branch, I override the Ext.Editor field like this:



Ext.override(Ext.Editor, {
onSpecialKey : function(field, e){
var key = e.getKey(),
complete = this.completeOnEnter && key == e.ENTER,
cancel = this.cancelOnEsc && key == e.ESC;

if(complete){
e.stopEvent();
this.completeEdit();
}else if(cancel){
e.stopEvent(); // Stop FF grid view scrolling to the top
this.cancelEdit();
}


if(field.triggerBlur && (complete || cancel)){
field.triggerBlur();
}
var processSpecialKey = true;
// If it is an composite field and tab is processed, handle onSpecialKey
// This is a hacked version as we are assuming that onSpecialKey function will handle it
var x = this.field !== field && key == e.TAB && typeof this.field.onSpecialKey == 'function';
if(x) {
this.field.onSpecialKey(field, e);
var ev = e.browserEvent;
if(ev.returnValue === false) {
processSpecialKey= false;
}
}
if(processSpecialKey) {
this.fireEvent('specialkey', field, e);
}
}
});


This is a bad hack but works in my tests. So, I would like to know if you have any other suggestions.

durlabh
16 Oct 2009, 1:52 PM
In my further testing, I found that this hack doesn't work correctly. Still looking for solutions...

dorgan
4 Nov 2009, 1:08 PM
I notice a very simple bug today. If you have allowBlank: false in both the timeConfig and dateConfig and you click on the timefield first and change focus it only highlights the time field as being invalid but if you select the datefield first and then change focus its highlights both fields as being in valid. I would assume the expected result would be to highlight both.

jsakalos
5 Nov 2009, 2:33 AM
Try to add


onBlur:function(f) {
this.validate();
// ...
and let me know if it helps please.

dorgan
5 Nov 2009, 6:08 AM
where am i adding that, to my object or somewhere in the extension.

jsakalos
5 Nov 2009, 6:42 AM
At the beginning of DateTimeField::onBlur method i.e. in the extension.

dorgan
5 Nov 2009, 7:25 AM
That seemed to take care of it.

jsakalos
5 Nov 2009, 12:17 PM
Very good! No side effect? Nothing else broken?

dorgan
5 Nov 2009, 12:39 PM
not that I can tell....everything else appears to be working.

jsakalos
5 Nov 2009, 12:39 PM
One thing more. It seems that validate() call should come after the values are updated. So the onBlur should be:


/**
* @private Handles blur event
*/
,onBlur:function(f) {
// called by both DateField and TimeField blur events

// revert focus to previous field if clicked in between
if(this.wrapClick) {
f.focus();
this.wrapClick = false;
}

// update underlying value
if(f === this.df) {
this.updateDate();
}
else {
this.updateTime();
}
this.updateHidden();

this.validate();

// fire events later
(function() {
if(!this.df.hasFocus && !this.tf.hasFocus) {
var v = this.getValue();
if(String(v) !== String(this.startValue)) {
this.fireEvent("change", this, v, this.startValue);
}
this.hasFocus = false;
this.fireEvent('blur', this);
}
}).defer(100, this);

} // eo function onBlur

Can you please test it?

dorgan
5 Nov 2009, 1:52 PM
result is as expected.

cscagliola
5 Nov 2009, 4:41 PM
I ran into a problem trying to use the plugin. If the JSON date is formatted '3/22/2009 10:35:31 AM', Date.parseDate(val, this.hiddenFormat) in the setValue function returned undefined even though it looks like a regular date (Locale 1033). So I started playing around with datejs lib, but as there are compatibility issues and additional script load, I dropped the idea and went on 'tinkering' with extJS. Long story short: replacing


else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat)
} with




else if('string' === typeof val && this.hiddenFormat) {

val = new Date(val);
val = val.dateFormat(this.hiddenFormat)
val = Date.parseDate(val, this.hiddenFormat)
}


in the setValue function fixed the problem. No testing done with other input formats but I assume that should help in other cases as well (such as different Session.LCIDs for me as old school classic ASP programmer :-?).

As I use the same format for the xtype: datefield and have the same problem there as well, I guess the same trick will do there too.

Please let me know if my code has a downside I am not aware of.

Thanx for your many and invaluable contributions to the forum jsakalos (http://www.extjs.com/forum/member.php?u=2178). Keep up the great work, saved me hours!!

cscagliola
5 Nov 2009, 5:31 PM
As I use the same format for the xtype: datefield and have the same problem there as well, I guess the same trick will do there too.
Indeed, the following solved my problem (related issue posted here (http://www.extjs.com/forum/showthread.php?t=83268)) by overriding the setValue function of Ext.Form.DateField:



Ext.override(Ext.ux.form.XDateField , {

setValue : function(date){
date = new Date(date);
date = date.dateFormat(this.format)
date = Date.parseDate(date, this.format)
return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(date));
}

}); Works for my needs....

igreg
6 Nov 2009, 2:35 AM
JsLint error on rev 749


DateTime.js (527): lint warning: missing semicolon
}
........^

patch :

else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat);
}

jsakalos
7 Nov 2009, 8:26 AM
@cscagliola,

the problem is that new Date('2009-04-04 12:34:56') produces error, but Date.parseDate('2009-04-04 12:34:56', 'Y-m-d H:i:s') produces the correct result. Your solution is therefore limited only to your format. What I needed was no format conversion between MySQL format and Date(Time)Field. BTW, if your hiddenFormat matches the format delivered from the server you shouldn't have any problems.

abraxxa
16 Nov 2009, 7:32 AM
@saki: I think I found an error in initComponent for the dateConfig:

,width:this.timeWidth
should be

,width:this.dateWidth

jsakalos
16 Nov 2009, 8:00 AM
No, that is not a bug. Underlying fields have same size initially. When anchored, or if you set a width of the whole DateTime, then dateWidth is calculated as width - timeWidth. See DateTime::setSize for more details.

jsakalos
16 Nov 2009, 8:02 AM
The bug reported here http://www.extjs.com/forum/showthread.php?t=82953 is fixed. Re-download the code from the first post please.

abraxxa
16 Nov 2009, 8:14 AM
Thanks for clearing that up so quickly!

radtad
17 Nov 2009, 6:52 PM
Maybe I'm missing something, but I would expect the combo box for selecting a time would fire off an 'expand' and 'collapse' which is what a typical combo box does. Is there a reason it doesn't?

I need to create a listener on 'collapse' which would then set another DateTime field to one hour later. On 'blur' will not work for me because this is in a form with a submit button. If the user selects the first DateTime and then clicks on the submit button there will be no real indication to the end user that the latter DateTime field has changed (because the only feasible place to put the listener is on blur and that will only show it for a split second).

Also, changing my listener to 'valid' is not acceptable because I don't want the latter DateTime field changed unless the user specifically changes the first one i.e. expand combo and collapse combo.

jsakalos
18 Nov 2009, 2:39 AM
Use Ext.util.Observable.capture to see all events that are fired. Also, keep in mind that this is a composite field so you may need to listen to underlying Date/TimeField events.

tigerfoot
18 Nov 2009, 9:19 AM
Hi Saki,

I'm trying (as a newbie can) your DateTime in a gridEditor.
With the help of the closed thread I've removed all initialisation I've before.

Now I'm just in face of a non rendering field.

Here some informations :
ExtJs 3.0 normal release
Json received response.
{"records":[{"no_point":"16.2","date_etabl":"2002-04-09 00:00:00",
"type_determination_id":"1","y":"596515.7700","x":"240111.3500","h_protection":"505.5900",
"h_equipement":null,"h_terrain":null,"gis_forage":null,"incl_forage":null,
"gis_equipement":null,"m_etabl":"N0917"}],"totalCount":"1176","success":true,"message":"load successfull","recordsCount":1}

The Json Reader ...
var detail_reader = new Ext.data.JsonReader({
totalProperty: 'totalCount',
successProperty: 'success',
idProperty: 'no_point',
root: 'records'
}, [
{name: 'no_point', type: 'string'},
{name: 'date_etabl', type: 'date', allowBlank: false},
{name: 'type_determination_id', type: 'float', allowBlank: false},
{name: 'x', type: 'float', allowBlank: false},
{name: 'y', type: 'float', allowBlank: false},
{name: 'h_protection', type: 'float', allowBlank: false},
{name: 'h_equipement', type: 'float', allowBlank: false},
{name: 'h_terrain', type: 'float', allowBlank: false},
{name: 'gis_forage', type: 'float', allowBlank: false},
{name: 'incl_forage', type: 'float', allowBlank: false},
{name: 'gis_equipement', type: 'float', allowBlank: false},
{name: 'm_etabl', type: 'string', allowBlank: false},
]);

And the associated column model (only for date_etabl shown ..)
{header: "Date établ.", width: 44, sortable: true, dataIndex: 'date_etabl', editor: DdateField, allowBlank: false, align:'left'}

Expected result ideally would be 09.04.2002 00:00:00
I've a blank field, and when I trigger the edition mode of the grid, date is set to today and time is blank.

Have you an idea of what I've missed ?
Any advise would really be appreciate.

abraxxa
18 Nov 2009, 9:21 AM
That's because it's called Ext.ux.form.DateTime

tigerfoot
18 Nov 2009, 10:09 AM
Thanks to your quick answer, but after sketching my head against a wall, I've discover my mistake.

I've edited my post with the last detail not working.

abraxxa
18 Nov 2009, 10:54 AM
You've configured

totalProperty: 'totalCount',
but return

"recordsCount":1
in your json

tigerfoot
18 Nov 2009, 11:08 AM
In fact TotalCount is set it's the global count of record, recordsCount is also set and is the resulting record .
But I see the returned record, only the date_etabl field with datetime ux is not show.

abraxxa
18 Nov 2009, 11:40 AM
Sorry, I've overlooked that.

Why do you think this has something to do with this ux?

editor: DdateField

mebuzzme
18 Nov 2009, 12:32 PM
Just wondering if this extension is compatible with EXTJS 3.0?

Thanks

Kevin

tigerfoot
18 Nov 2009, 12:34 PM
Rah If I forget the important code part, It's sure you wouldn't understand :-))
2 lines before the column model ...
var DdateField = new Ext.ux.form.DateTime();

jsakalos
18 Nov 2009, 5:00 PM
I should work with Ext 3.x; I haven't thoroughly tested it, though.

tigerfoot
19 Nov 2009, 12:43 AM
I've made some progress (under extjs 3.0). And now it display inside the grid.
For this working I've to remove any type inside the JsonReader so now the filed is declared like this one
{name: 'date_etabl'},

It display as '2002-04-09 00:00:00' ( ok for me ) but the last annoying thing is when I edit the row (GridEditor) the date is displayed as 09/04/02 and I would prefer the used locale here and have 09.04.2002
also the time is display as 12:00 AM and it should be show as 00:00:00 ( the H:i format )

Where can I place the date & time format ?

abraxxa
19 Nov 2009, 12:49 AM
Just look at the source! It's just javascript!
dateFormat and timeFormat are the attributes.

tigerfoot
19 Nov 2009, 1:11 AM
Yeap that's my first reflex. So I've dicovered the 2 attributes
I've try to set them inside the JsonReader dateFormat:'d.m.Y', timeFormat:'H:i:s' or inside the column model with no result.
Of course if I change it directly inside the Ext.ux.form.DateTime it work as I want ( but frankly it's not the best way :-)

abraxxa
19 Nov 2009, 1:55 AM
You want to change the DateTime object, so you have to set it there!
It has only to do with how the date and time is displayed in the DateTime ux, not with the format of the data coming into the store.

tigerfoot
19 Nov 2009, 2:11 AM
Tanks I understand a bit more now :-)

jsakalos
19 Nov 2009, 4:54 AM
@abraxxa, thank you for helping tigerfoot with my extension.

:) :) :)

tigerfoot
19 Nov 2009, 5:01 AM
Yeah :-)
And the good news, it's seems to work nicely with ExtJs 3.0 ...

Now I'm going to face the other challenge ( Filter, remote sorting, search ) .... You would see me certainly in a very near future on forums ...

abraxxa
24 Nov 2009, 3:53 AM
That's an excerpt of my code. nothing fancy, just a toolbar used as top-toolbar of a panel:



var tb_graphs = new Ext.Toolbar({
items:[
'limit [kbit/s]:', {
xtype:'numberfield',
id:'field_limit',
allowBlank:true,
allowDecimals:false,
allowNegative:false,
emptyText:'dynamic',
maxLength:8,
name:'limit',
width:100
},
'-',
'',
'start:', {
xtype:'xdatetime',
id:'field_startdate',
name:'datetime_start',
dateFormat:Date.patterns.Date,
dateConfig:{
allowBlank:false,
emptyText:'YYYY-MM-DD'
},
timeFormat:Date.patterns.TimeShort,
timeConfig:{
allowBlank:false,
emptyText:'HH:MM',
increment:60
},
// default is yesterday
value:new Date().add(Date.DAY, -1)
},
'-',
{
text:'refresh',
handler:updateGraph
},
'->'
]
});

var panel_interface = new Ext.Panel({
title:'Interface [% interface.portname %]',
iconCls:'icon-interface',
collapsible:false,
tbar:tb_interface,
items:[
],
renderTo:'main'
});


I still have the issue that the timefield overlapps the datefield when used in a top toolbar of a panel which is collapsed initially.
Can you please take a look?

realjax
24 Nov 2009, 4:05 AM
Saki,

On using the DateTime field plugin in a form I've noticed that form.isDirty() only returns true after the focus is moved from the dateTime field.
So, when a user is presented with an empty form and clicks the little calendar icon to select a date, the selected date becomes visible in the field. But at this stage (without effectively selecting a time) the form.isDirty() method still returns false..

Is this by design?

jsakalos
24 Nov 2009, 4:35 AM
Does it differ from normal DateField behavior?

realjax
24 Nov 2009, 7:52 AM
Yes. The normal DateField works as expected..

jsakalos
24 Nov 2009, 11:28 AM
OK, post please a simple showcase, I'll take a look.

Stefan B
3 Dec 2009, 7:34 AM
Resetting an XDateTime field does not work currently, both date and time field sub components are reset to an empty value instead of to the last loaded value of the composite field.

I found that this is due to the fact that DateTime.reset simply calls reset on the underlying Ext.form.DateField and Ext.form.TimeField. That will not work because originalValue is never propagated to them. The reset method should look like this instead:


...
reset: function() {
this.df.setValue(this.originalValue);
this.tf.setValue(this.originalValue);
},
...

Regards,
Stefan

abraxxa
3 Dec 2009, 7:47 AM
I'd say the originalValue should be propagated instead.

jsakalos
3 Dec 2009, 8:03 AM
Resetting an XDateTime field does not work currently, both date and time field sub components are reset to an empty value instead of to the last loaded value of the composite field.

I found that this is due to the fact that DateTime.reset simply calls reset on the underlying Ext.form.DateField and Ext.form.TimeField. That will not work because originalValue is never propagated to them. The reset method should look like this instead:


...
reset: function() {
this.df.setValue(this.originalValue);
this.tf.setValue(this.originalValue);
},
...Regards,
Stefan

Thank you for the fix. Have you tested it?

Stefan B
3 Dec 2009, 8:16 AM
@jsakalos:

Yes, it works both with initially set values as well as with values set by a form load action.


@abraxxa:

That would only work with initially set values. If you load a form with trackResetOnLoad == TRUE it's out of your hand since originalValue (inherited by Ext.form.Field) is just a property and has no setter that you could use to propagate the new originalValue to the sub components. This is a problem shared by all all composite fields.

abraxxa
3 Dec 2009, 8:19 AM
@Stefan B: thanks for the clarification!

jsakalos
3 Dec 2009, 8:25 AM
OK, updating source.

snehat
19 Dec 2009, 1:45 AM
Hi All,

DateTimeField does not select time. It selects 00.00.00 always.
I am not able to identify the problem.

Thanks & Regards,
Sneha

jsakalos
19 Dec 2009, 2:59 AM
Are you sure this is the right thread? My DateTime looks different.

abraxxa
19 Dec 2009, 11:24 AM
@saki: FYI it works with ExtJS 3.1.0 without changes and the display bug of overlapping date and time field in a toolbar of a panel which is initially collapsed is gone. ;)

jsakalos
19 Dec 2009, 2:09 PM
Thank you very much for this good news!!!

:) :) :)

ozum
25 Jan 2010, 2:55 PM
Edit: Original message deleted.

@jsakalos: Great plugin. Thank you for sharing. I've made some additions, and want to share here. I hope it wasn't discussed here before, because I couldn't read whole 60+ page thread. (:|

Added:
minValue, maxValue: For combined datetime value not for separate date config and time config field. Works with existing config values. Now it is possible to set minValue : '2010-01-20 16:00:00', and users can select 15:00:00 clock for January 21 but not for January 20 (marks as invalid).

format: Format of combined date time field. Used to show formatted datetime in invalid message. Can be localized. (default: 'm/d/y g:i A')

validatorFormat: Format used for combined minValue and maxValue constraints. (default: 'Y-m-d H:i:s')

startDateField, endDateField: Inspired heavily :"> from http://www.extjs.com/deploy/dev/exam...dv-vtypes.html (http://www.extjs.com/forum/../deploy/dev/examples/form/adv-vtypes.html). Two datetime fields acting as a date range. Selecting an initial datetime sets the minimum value for the end field and vice versa.

disabled behaviour is changed: In original version, date time is sent as parameter during post even the field is disabled. Now it doesn't.

I used standard date error messages to let developers utilize existing translations.

Tested on Windows 7 with Firefox 3.6, IE 8 and Chrome 3.0.

Please suggest any better practice or change if necessary, because I'm new to ExtJS.

Below is changed parts and sample usage config:




// vim: ts=4:sw=4:nu:fdc=4:nospell
/*global Ext */
/**
* @class Ext.ux.form.DateTime
* @extends Ext.form.Field
*
* DateTime field, combination of DateField and TimeField
*
* @author Ing. Jozef Sakáloš
* @copyright (c) 2008, Ing. Jozef Sakáloš
* @version 2.0
* @revision $Id: Ext.ux.form.DateTime.js 787 2009-12-03 16:24:21Z jozo $
*
* @license Ext.ux.form.DateTime is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
* target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
*
* @forum 22661
*
* @donate
* <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
* <input type="hidden" name="cmd" value="_s-xclick">
* <input type="hidden" name="hosted_button_id" value="3430419">
* <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif"
* border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
* <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
* </form>
*/

Ext.ns('Ext.ux.form');

/**
* Creates new DateTime
* @constructor
* @param {Object} config A config object
*/
Ext.ux.form.DateTime = Ext.extend(Ext.form.Field, {
/**
* @cfg {Function} dateValidator A custom validation function to be called during date field
* validation (defaults to null)
*/
dateValidator:null
/**
* @cfg {String/Object} defaultAutoCreate DomHelper element spec
* Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
*/
,defaultAutoCreate:{tag:'input', type:'hidden'}
/**
* @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
*/
,dtSeparator:' '
/**
* @cfg {String} hiddenFormat Format of datetime used to store value in hidden field
* and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql format)
*/
,hiddenFormat:'Y-m-d H:i:s'
/**
* @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
*/
,otherToNow:true
/**
* @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty value.
* If it is true then setValue() sets value of field to current date and time (defaults to false)
*/
/**
* @cfg {String} timePosition Where the time field should be rendered. 'right' is suitable for forms
* and 'below' is suitable if the field is used as the grid editor (defaults to 'right')
*/
,timePosition:'right' // valid values:'below', 'right'
/**
* @cfg {Function} timeValidator A custom validation function to be called during time field
* validation (defaults to null)
*/
,timeValidator:null
/**
* @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
*/
,timeWidth:100
/**
* @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
*/
,dateFormat:'m/d/y'
/**
* @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
*/
,timeFormat:'g:i A'
/**
* @cfg {Object} dateConfig Config for DateField constructor.
*/
/**
* @cfg {Object} timeConfig Config for TimeField constructor.
*/
/**
* @cfg {String} format Format of DateTime. Used to show formatted datetime in invalid message. Can be localized.
*/
,format: 'm/d/y g:i A'
/**
* @cfg {String} validatorFormat Format for DateTime validator. Used to validate formatted datetime. Can be localized.
*/
,validatorFormat: 'Y-m-d H:i:s'
// {{{
/**
* @private
* creates DateField and TimeField and installs the necessary event handlers
*/
,initComponent:function() {
// call parent initComponent
Ext.ux.form.DateTime.superclass.initComponent.call(this);

// create DateField
var dateConfig = Ext.apply({}, {
id:this.id + '-date'
,format:this.dateFormat || Ext.form.DateField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.dateValidator
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.dateConfig);
this.df = new Ext.form.DateField(dateConfig);
this.df.ownerCt = this;
delete(this.dateFormat);

// create TimeField
var timeConfig = Ext.apply({}, {
id:this.id + '-time'
,format:this.timeFormat || Ext.form.TimeField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.timeValidator
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.timeConfig);
this.tf = new Ext.form.TimeField(timeConfig);
this.tf.ownerCt = this;
delete(this.timeFormat);

// relay events
this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);
this.on('specialkey', this.onSpecialKey, this);

} // eo function initComponent
// }}}
// {{{
/**
* @private
* Renders underlying DateField and TimeField and provides a workaround for side error icon bug
*/
,onRender:function(ct, position) {
// don't run more than once
if(this.isRendered) {
return;
}

// render underlying hidden field
Ext.ux.form.DateTime.superclass.onRender.call(this, ct, position);

// render DateField and TimeField
// create bounding table
var t;
if('below' === this.timePosition || 'bellow' === this.timePosition) {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[{tag:'td', style:'padding-bottom:1px', cls:'ux-datetime-date'}]}
,{tag:'tr',children:[{tag:'td', cls:'ux-datetime-time'}]}
]}, true);
}
else {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[
{tag:'td',style:'padding-right:4px', cls:'ux-datetime-date'},{tag:'td', cls:'ux-datetime-time'}
]}
]}, true);
}

this.tableEl = t;
this.wrap = t.wrap({cls:'x-form-field-wrap'});
// this.wrap = t.wrap();
this.wrap.on("mousedown", this.onMouseDown, this, {delay:10});

// render DateField & TimeField
this.df.render(t.child('td.ux-datetime-date'));
this.tf.render(t.child('td.ux-datetime-time'));

// workaround for IE trigger misalignment bug
// see http://extjs.com/forum/showthread.php?p=341075#post341075
// if(Ext.isIE && Ext.isStrict) {
// t.select('input').applyStyles({top:0});
// }

this.df.el.swallowEvent(['keydown', 'keypress']);
this.tf.el.swallowEvent(['keydown', 'keypress']);

// create icon for side invalid errorIcon
if('side' === this.msgTarget) {
var elp = this.el.findParent('.x-form-element', 10, true);
if(elp) {
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
}

var o = {
errorIcon:this.errorIcon
,msgTarget:'side'
,alignErrorIcon:this.alignErrorIcon.createDelegate(this)
};
Ext.apply(this.df, o);
Ext.apply(this.tf, o);
// this.df.errorIcon = this.errorIcon;
// this.tf.errorIcon = this.errorIcon;
}

// setup name for submit
this.el.dom.name = this.hiddenName || this.name || this.id;

// prevent helper fields from being submitted
this.df.el.dom.removeAttribute("name");
this.tf.el.dom.removeAttribute("name");

// we're rendered flag
this.isRendered = true;

// update hidden field
this.updateHidden();

if (this.minValue) { this.setMinValue(this.minValue); }
if (this.maxValue) { this.setMaxValue(this.maxValue); }
} // eo function onRender
// }}}
// {{{
/**
* @private
*/
,adjustSize:Ext.BoxComponent.prototype.adjustSize
// }}}
// {{{
/**
* @private
*/
,alignErrorIcon:function() {
this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
}
// }}}
// {{{
/**
* @private initializes internal dateValue
*/
,initDateValue:function() {
this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
}
// }}}
// {{{
/**
* Calls clearInvalid on the DateField and TimeField
*/
,clearInvalid:function(){
this.df.clearInvalid();
this.tf.clearInvalid();
} // eo function clearInvalid
// }}}
// {{{
/**
* Calls markInvalid on both DateField and TimeField
* @param {String} msg Invalid message to display
*/
,markInvalid:function(msg){
this.df.markInvalid(msg);
this.tf.markInvalid(msg);
} // eo function markInvalid
// }}}
// {{{
/**
* @private
* called from Component::destroy.
* Destroys all elements and removes all listeners we've created.
*/
,beforeDestroy:function() {
if(this.isRendered) {
// this.removeAllListeners();
this.wrap.removeAllListeners();
this.wrap.remove();
this.tableEl.remove();
this.df.destroy();
this.tf.destroy();
}
} // eo function beforeDestroy
// }}}
// {{{
/**
* Disable this component.
* @return {Ext.Component} this
*/
,disable:function() {
if(this.isRendered) {
this.df.disabled = this.disabled;
this.df.onDisable();
this.tf.onDisable();
}
this.disabled = true;
this.df.disabled = true;
this.tf.disabled = true;
// this.fireEvent("disable", this);
Ext.ux.form.DateTime.superclass.disable.call(this);
return this;
} // eo function disable
// }}}
// {{{
/**
* Enable this component.
* @return {Ext.Component} this
*/
,enable:function() {
if(this.rendered){
this.df.onEnable();
this.tf.onEnable();
}
this.disabled = false;
this.df.disabled = false;
this.tf.disabled = false;
// this.fireEvent("enable", this);
Ext.ux.form.DateTime.superclass.enable.call(this);
return this;
} // eo function enable
// }}}
// {{{
/**
* @private Focus date filed
*/
,focus:function() {
this.df.focus();
} // eo function focus
// }}}
// {{{
/**
* @private
*/
,getPositionEl:function() {
return this.wrap;
}
// }}}
// {{{
/**
* @private
*/
,getResizeEl:function() {
return this.wrap;
}
// }}}
// {{{
/**
* @return {Date/String} Returns value of this field
*/
,getValue:function() {
// create new instance of date
return this.dateValue ? new Date(this.dateValue) : '';
} // eo function getValue
// }}}
// {{{
/**
* @return {Boolean} true = valid, false = invalid
* @private Calls isValid methods of underlying DateField and TimeField and returns the result
*/
//,isValid:function() {
// return this.df.isValid() && this.tf.isValid();
//} // eo function isValid
// }}}
// {{{
/**
* Returns true if this component is visible
* @return {boolean}
*/
,isVisible : function(){
return this.df.rendered && this.df.getActionEl().isVisible();
} // eo function isVisible
// }}}
// {{{
/**
* @private Handles blur event
*/
,onBlur:function(f) {
// called by both DateField and TimeField blur events

// revert focus to previous field if clicked in between
if(this.wrapClick) {
f.focus();
this.wrapClick = false;
}

// update underlying value
if(f === this.df) {
this.updateDate();
}
else {
this.updateTime();
}
this.updateHidden();

this.validate();

// fire events later
(function() {
if(!this.df.hasFocus && !this.tf.hasFocus) {
var v = this.getValue();
if(String(v) !== String(this.startValue)) {
this.fireEvent("change", this, v, this.startValue);
}
this.hasFocus = false;
this.fireEvent('blur', this);
}
}).defer(100, this);

} // eo function onBlur
// }}}
// {{{
/**
* @private Handles focus event
*/
,onFocus:function() {
if(!this.hasFocus){
this.hasFocus = true;
this.startValue = this.getValue();
this.fireEvent("focus", this);
}
}
// }}}
// {{{
/**
* @private Just to prevent blur event when clicked in the middle of fields
*/
,onMouseDown:function(e) {
if(!this.disabled) {
this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
}
}
// }}}
// {{{
/**
* @private
* Handles Tab and Shift-Tab events
*/
,onSpecialKey:function(t, e) {
var key = e.getKey();
if(key === e.TAB) {
if(t === this.df && !e.shiftKey) {
e.stopEvent();
this.tf.focus();
}
if(t === this.tf && e.shiftKey) {
e.stopEvent();
this.df.focus();
}
this.updateValue();
}
// otherwise it misbehaves in editor grid
if(key === e.ENTER) {
this.updateValue();
}

} // eo function onSpecialKey
// }}}
// {{{
/**
* Resets the current field value to the originally loaded value
* and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
*/
,reset:function() {
this.df.reset(this.originalValue);
this.tf.reset(this.originalValue);
} // eo function reset
// }}}
// {{{
/**
* @private Sets the value of DateField
*/
,setDate:function(date) {
this.df.setValue(date);
} // eo function setDate
// }}}
// {{{
/**
* @private Sets the value of TimeField
*/
,setTime:function(date) {
this.tf.setValue(date);
} // eo function setTime
// }}}
// {{{
/**
* @private
* Sets correct sizes of underlying DateField and TimeField
* With workarounds for IE bugs
*/
,setSize:function(w, h) {
if(!w) {
return;
}
if('below' === this.timePosition) {
this.df.setSize(w, h);
this.tf.setSize(w, h);
if(Ext.isIE) {
this.df.el.up('td').setWidth(w);
this.tf.el.up('td').setWidth(w);
}
}
else {
this.df.setSize(w - this.timeWidth - 4, h);
this.tf.setSize(this.timeWidth, h);

if(Ext.isIE) {
this.df.el.up('td').setWidth(w - this.timeWidth - 4);
this.tf.el.up('td').setWidth(this.timeWidth);
}
}
} // eo function setSize
// }}}
// {{{
/**
* @param {Mixed} val Value to set
* Sets the value of this field
*/
,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat);
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}
this.updateValue();
} // eo function setValue
// }}}
// {{{
/**
* Hide or show this component by boolean
* @return {Ext.Component} this
*/
,setVisible: function(visible){
if(visible) {
this.df.show();
this.tf.show();
}else{
this.df.hide();
this.tf.hide();
}
return this;
} // eo function setVisible
// }}}
//{{{
,show:function() {
return this.setVisible(true);
} // eo function show
//}}}
//{{{
,hide:function() {
return this.setVisible(false);
} // eo function hide
//}}}
// {{{
/**
* @private Updates the date part
*/
,updateDate:function() {

var d = this.df.getValue();
if(d) {
if(!(this.dateValue instanceof Date)) {
this.initDateValue();
if(!this.tf.getValue()) {
this.setTime(this.dateValue);
}
}
this.dateValue.setMonth(0); // because of leap years
this.dateValue.setFullYear(d.getFullYear());
this.dateValue.setMonth(d.getMonth(), d.getDate());
// this.dateValue.setDate(d.getDate());
}
else {
this.dateValue = '';
this.setTime('');
}
} // eo function updateDate
// }}}
// {{{
/**
* @private
* Updates the time part
*/
,updateTime:function() {
var t = this.tf.getValue();
if(t && !(t instanceof Date)) {
t = Date.parseDate(t, this.tf.format);
}
if(t && !this.df.getValue()) {
this.initDateValue();
this.setDate(this.dateValue);
}
if(this.dateValue instanceof Date) {
if(t) {
this.dateValue.setHours(t.getHours());
this.dateValue.setMinutes(t.getMinutes());
this.dateValue.setSeconds(t.getSeconds());
}
else {
this.dateValue.setHours(0);
this.dateValue.setMinutes(0);
this.dateValue.setSeconds(0);
}
}
} // eo function updateTime
// }}}
// {{{
/**
* @private Updates the underlying hidden field value
*/
,updateHidden:function() {
if(this.isRendered) {
var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
this.el.dom.value = value;
if (this.dateValue instanceof Date) {
if (this.startDateField && (!this.dateRangeMax || (this.dateValue.getTime() != this.dateRangeMax))) {
var start = Ext.getCmp(this.startDateField);
start.setMaxValue(this.dateValue);
this.dateRangeMax = this.dateValue.getTime(); // Clone instead of ref.
start.validate();
}
else if (this.endDateField && (!this.dateRangeMin || (this.dateValue.getTime() != this.dateRangeMin))) {
var end = Ext.getCmp(this.endDateField);
end.setMinValue(this.dateValue);
this.dateRangeMin = this.dateValue.getTime(); // // Clone instead of ref.
end.validate();
}
}
}
}
// }}}
// {{{
/**
* @private Updates all of Date, Time and Hidden
*/
,updateValue:function() {

this.updateDate();
this.updateTime();
this.updateHidden();

return;
} // eo function updateValue
// }}}

// {{{
/**
* Updates min value for combined field and date field. Doesn't change
* time field's.
*/
,setMinValue:function(minValue) {
if (! (minValue instanceof Date)) {
// Make sure they are in the same time zone.
minValue = Date.parseDate(minValue, this.validatorFormat, true);
if (! (minValue instanceof Date)) {
alert("Error: Can't parse minValue '" + minValue + "' on " + this.name + " with format: " + this.validatorFormat );
return false;
}
}

this.minValue = minValue;
//Ext.ux.form.DateTime.superclass.setMinValue.call(this,minValue);
// Set datefield's min value, but NOT timefield's.
// For minValue of 20.01.2010 13:00, the value of 22.01.2010 11:00 is valid.
// Note if we set timefield's minValue 11:00 can not be selected even for later days.
var minDate = new Date(minValue.getFullYear(), minValue.getMonth(), minValue.getDate(), 0,0,0,0 );
this.df.setMinValue(minDate);
} // }}} eo function setMinValue
// {{{
/**
* Updates max value for combined field and date field. Doesn't change
* time field's.
*/
,setMaxValue:function(maxValue) {
if (! (maxValue instanceof Date)) {
// Make sure they are in the same time zone.
maxValue = Date.parseDate(maxValue, this.validatorFormat, true);
if (! (maxValue instanceof Date)) {
alert("Error: Can't parse maxValue '" + maxValue + "' on " + this.name + " with format: " + this.validatorFormat );
return false;
}
}
this.maxValue = maxValue;
//Ext.ux.form.DateTime.superclass.setMaxValue.call(this,maxValue);
// Set datefield's min value, but NOT timefield's.
// For minValue of 20.01.2010 13:00, the value of 22.01.2010 11:00 is valid.
// Note if we set timefield's minValue 11:00 can not be selected even for later days.
var maxDate = new Date(maxValue.getFullYear(), maxValue.getMonth(), maxValue.getDate(), 0,0,0,0 );
this.df.setMaxValue(maxDate);
}
// }}} eo function setMaxValue

// {{{
/**
* @return {Boolean} true = valid, false = invalid
* calls validate methods of DateField and TimeField
*/
,validate:function() {
// return this.df.validate() && this.tf.validate();
// Ext.ux.form.DateTime.superclass.validate.call(this);
var value_dt = this.getValue();

if (! (this.df.validate() && this.tf.validate()) ) { return false; }
if (! this.df.value || ! this.tf.value ) { return true; }

if (this.minValue != null) {
var min_dt;

if (this.minValue instanceof Date) {
min_dt = this.minValue;
}
else {
// Make sure they are in the same time zone.
min_dt = Date.parseDate(this.minValue + value_dt.format('O'), this.validatorFormat+'O', true);
if (! (min_dt instanceof Date)) {
alert("Error: Can't parse minValue '" + this.minValue + "' on " + this.name + " with format: " + this.validatorFormat );
return false;
}
}
if ( value_dt < min_dt || (value_dt <= min_dt && this.startDateField) ) { // Two fields shouldn't be equal
this.markInvalid(String.format(Ext.form.DateField.prototype.minText, min_dt.format(this.format)) );
return false;
}
}
if (this.maxValue != null) {
var max_dt;

if (this.maxValue instanceof Date) {
max_dt = this.maxValue;
}
else {
// Make sure they are in the same time zone.
max_dt = Date.parseDate(this.maxValue + value_dt.format('O'), this.validatorFormat+'O', true);
if (! (max_dt instanceof Date)) {
alert("Error: Can't parse maxValue '" + this.maxValue + "' on " + this.name + " with format: " + this.validatorFormat);
return false;
}

}
if (value_dt > max_dt || (value_dt >= max_dt && this.endDateField) ) { // Two fields shouldn't be equal
this.markInvalid(String.format(Ext.form.DateField.prototype.maxText, max_dt.format(this.format)) );
return false;
}
}
return true;
} // eo function validate
// }}}
// {{{
/**
* Returns renderer suitable to render this field
* @param {Object} Column model config
*/
,renderer: function(field) {
var format = field.editor.dateFormat || Ext.ux.form.DateTime.prototype.dateFormat;
format += ' ' + (field.editor.timeFormat || Ext.ux.form.DateTime.prototype.timeFormat);
var renderer = function(val) {
var retval = Ext.util.Format.date(val, format);
return retval;
};
return renderer;
} // eo function renderer
// }}}

}); // eo extend

// register xtype
Ext.reg('xdatetime', Ext.ux.form.DateTime);


Sample Config:



{
"validatorFormat": "d.m.Y H:i",
"endDateField": "E8872E10-6BF6-1014-A5EC-A2B507540FFF",
// "maxValue": "15.01.2010 08:00", // To provide maximum Value. Look "validatorFormat"
"hiddenFormat": "d.m.Y H:i",
"xtype": "xdatetime",
"dateFormat": "d.m.Y",
"timeFormat": "H:i",
"fieldLabel": "Meeting Start",
"id": "E8872C2C-6BF6-1014-A5EC-A2B507540FFF",
"value": "",
"name": "meeting_start",
"format": "d.m.Y H:i",
"timeConfig": {
// "minValue": "08:00", // To limit working hours etc.
// "minValue": "19:00", // To limit working hours etc.
"maskRe": /[\d:]/
},
"dateConfig": {
"maskRe": /[\d\/.]/
}
},
{
"validatorFormat": "d.m.Y H:i",
"hiddenFormat": "d.m.Y H:i",
"xtype": "xdatetime",
"dateFormat": "d.m.Y",
"timeFormat": "H:i",
"fieldLabel": "Meeting End",
"id": "E8872E10-6BF6-1014-A5EC-A2B507540FFF",
"value": "",
"startDateField": "E8872C2C-6BF6-1014-A5EC-A2B507540FFF",
// "minValue": "10.01.2010 08:00", // To provide minimum Value. Look "validatorFormat"
"name": "meeting_end",
"format": "d.m.Y H:i",
"timeConfig": {
// "minValue": "08:00", // To limit working hours etc.
// "minValue": "19:00", // To limit working hours etc.
"maskRe": /[\d:]/
},
"dateConfig": {
"maskRe": /[\d\/.]/
}
}

jsakalos
26 Jan 2010, 1:51 AM
Thank you very much - I'll test it soon and I'll add it to the main code if I find not problems.

ozum
26 Jan 2010, 1:54 AM
That would be good.

harel
27 Jan 2010, 5:19 AM
Hi there,
First, I'll have to repeat what was said before - (!)Thanks(!) for this Saki, and all your other plugins, code, contributions, help and insight.

I have xdatetime in a grid. The problem I'm having is that if the grid's store loads a value, it displays correctly. If the value is empty to begin with, and I'm setting a date and time using the xdatetime field, I get an error and the grid's cell remains empty.
Do I need to manually set the grid's store with the value somehow?

Below is my config, renderer and error:

{ header: 'End Date', dataIndex: 'campaign-end_time', width:180, hidden: true,
renderer: GridRenderers.datetime ,
editor: {
xtype:'xdatetime',
timeWidth:80,
timeFormat: 'g:i A' ,
timeConfig: { altFormats:'H:i:s' ,allowBlank:false } ,
dateFormat: 'd/m/Y' ,
dateConfig: { altFormats:'Y-m-d|Y-n-d',allowBlank:true } } },

GridRenderers.datetime = function(value) {
if (value!='None' && value!='') {
return Ext.util.Format.date(value, 'd/m/Y g:i A');
} else {
return ''
}
},

this.dateValue.setMonth is not a function
http://localhost:8000/site_media/js/extensions/Ext.ux.form.DateTime.js
Line 596


Thanks,
Harel

Update: I've tried various combinations of types for the underlying Record of the grid (date, auto, no type). I've also logged what is provided to the setValue and setDate functions after I blur out of the date and time fields, and its always an empty string provided. However if the store initially had a date value, the fields work fine and I can update the value. This is a problem for initially empty columns.
I've been at this problem for hours now (with a client impatiently waiting for me to fix this). I've went through most pages of this thread but found no working answer for my scenario.

jsakalos
27 Jan 2010, 6:33 AM
Take a look at http://recordform.extjs.eu/ DateTime is used in the grid there.

harel
27 Jan 2010, 7:00 AM
Your demo of recordform is using build 770. The latest build in the first post is 787.
The problem is when adding a new date/time value to an empty field. I didn't try it but I bet if you change recordform to use datetime.js build 787 it will exhibit the same problem.

I'm reverting to 770 as this actually solved my problem!

Thanks!
Harel :D

jsakalos
27 Jan 2010, 10:23 AM
The difference of the revisions is:


p, li { white-space: pre-wrap; } ,reset:function() {
+ this.df.reset(this.originalValue);
+ this.tf.reset(this.originalValue);
- this.df.reset();
- this.tf.reset();
} // eo function reset
The change was requested and proposed by Stefan B here: http://www.extjs.com/forum/showthread.php?p=414925#post414925

Stefan, take a look again please and fix your fix. ;)

Stefan B
28 Jan 2010, 12:26 PM
Hi Saki,

not sure if this would fix the exact issue of the previous poster, but the change you made does not match my proposal :)

it should read

+ this.df.setValue(this.originalValue);
+ this.tf.setValue(this.originalValue);

instead of

+ this.df.reset(this.originalValue);
+ this.tf.reset(this.originalValue);

jsakalos
28 Jan 2010, 2:48 PM
I see, mea culpa.

@harel, can you implement Stefan B's change, try it and let me know if it works now?

ilevina
29 Jan 2010, 2:39 PM
Where from I can download new version of DateTime component:)

Thx,

Irene

jsakalos
29 Jan 2010, 2:45 PM
First post.

ilevina
29 Jan 2010, 3:13 PM
Sorry could you give link where from can be downloaded latest DateTime.js , I found some code on First post, not sure if this is new one (:

Thx for the help.

abraxxa
29 Jan 2010, 3:20 PM
Saki is the author of DateTime, he told you, I think you should trust him and use the version from page one.

jsakalos
29 Jan 2010, 3:27 PM
Here is the link: http://www.extjs.com/forum/showthread.php?t=22661

mrjoltcola
5 Feb 2010, 2:57 PM
Hi Saki,

Trying to use the DateTime extension in a Form with a JSON load, the value is not getting set for the component, so I'm getting 1970.

I added altFormats = 'c' as well (I'm using the ISO format)



{ fieldLabel:

'Date of Service', name: 'dateOfService',

xtype:


'xdatetime',

dateFormat: 'm/d/Y',
dateConfig: { altFormats:'c|M$|m/d/Y|m-d-Y', allowBlank:true },
timeFormat: 'h:i A'
},

My date string JSON looks like:
"dateOfService":"2010-02-05T14:45:00"
Any ideas?

Thanks for your help by the way, I sent you a steak dinner on me, or I hope so, I don't know what a Euro will buy nowadays.
-mjc

jsakalos
5 Feb 2010, 3:18 PM
Add full string to altFormats, not only date part.

mrjoltcola
5 Feb 2010, 3:21 PM
Add full string to altFormats, not only date part.

Not sure If I understand what you mean.

The formats 'c' and 'M$' are full timestamp formats and they work with JSON using the regular datetime field.

jsakalos
6 Feb 2010, 12:45 AM
I see. Does it work with core DateField and makes troubles only with DataTime?

mrjoltcola
6 Feb 2010, 8:35 AM
Yes. I haven't debugged it yet, but if my memory serves, it is DateField.setValue() that uses altFormats. My DateField overload adds 'c|M$' because in my projects I use either ISO or Microsoft formats for datetimes.

This is the DateField override I use for JSON de-serialization:



Ext.override(Ext.form.DateField, {
altFormats : "c|M$|m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|mdY|d|Y-m-d",
setValue : function(date) {
return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
}
});

mrjoltcola
6 Feb 2010, 11:03 AM
Hi Saki,

I did not notice your custom param, "hiddenFormat"

By setting the hiddenFormat attribute to 'c' my JSON date works fine now. I assumed overriding altFormats would work similar to DateField, that was my mistake by not reading your instructions.

Thanks.

mrjoltcola
6 Feb 2010, 11:06 AM
[EDITED - I mispoke about the other fields, they also resize]
Now I have a display issue. The DateTime control shrinks the date section when I resize the form, such that it is obscured. The date portion resizes, but the time does not. I try using a width value to set a minimum size for the field, but it does not resolve it? How do I fix this? I want the control always to show both date and time in completion, regardless of the form size.

Relevant section...



defaults: { anchor: '96%', labelWidth: 70, labelSeparator: '' },
items:
[
{ fieldLabel: 'Date of Service', name: 'dateOfService',
xtype: 'xdatetime',
dateFormat: 'm/d/Y',
hiddenFormat: 'c',
dateConfig: { altFormats:'m/d/Y|m-d-Y', allowBlank:true },
timeFormat: 'h:i A'
},

jsakalos
7 Feb 2010, 12:20 PM
The logic is: Time has fixed width and Date has Total width - Time width. You can set some width then or you can set min width of the window (if the form is in one).

Gonfi
25 Feb 2010, 5:50 AM
when i create a new Ext.ux.form.DateTime( myConfig ); where myConfig.value = some date object, and then in the next line i so a newField.getValue(), it's empty ''.

i have to do a setValue( some date object) and thus initialize the value again, right after the DateTime object creation, in order for getValue() to return the initially visible object at this stage.

(also, i would prefer a no-value to be returned as null instead of an empty string.)

abraxxa
2 Mar 2010, 7:08 AM
ExtJS 3.1.1 broke what 3.1.0 fixed, the time field overlapps the date field in a toolbar of a panel which is collapsed on pageload after expand.
That happened with 3.0.x too, but not with 3.1.0.
Do you have an idea how that can be fixed?

jsakalos
2 Mar 2010, 8:20 AM
Not yet - haven't seen it. A simple showcase?

abraxxa
2 Mar 2010, 9:11 AM
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<title>DateTime field in toolbar of collapsed panel</title>
</head>
<link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css" />
<body>
<div id="panel"></div>
</body>
<script type="text/javascript" src="extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="Ext.ux.form.DateTime.js"></script>
<script type="text/javascript">
Ext.onReady(function(){
new Ext.Panel({
title:'Testpanel',
collapsible:true,
collapsed:true,
renderTo:'panel',
tbar:[
'start:', {
xtype:'xdatetime',
id:'field_startdate',
name:'datetime_start',
dateFormat:'Y-m-d',
dateConfig:{
allowBlank:false,
emptyText:'YYYY-MM-DD'
},
timeFormat:'H:i',
timeConfig:{
allowBlank:false,
emptyText:'HH:MM',
increment:60
},
// default is yesterday
value:new Date().add(Date.DAY, -1)
}
]
});
});
</script>
</html>

jsakalos
2 Mar 2010, 3:45 PM
The overall width of DateTime is set too small - 17px. I don't know why yet.

abraxxa
8 Mar 2010, 8:39 AM
Did you have time to look at the problem?

Gonfi
8 Mar 2010, 9:37 AM
pls make a diff (attached file). i have added the config var 'useFullWidth' and made use of it in 2 places. would be nice if you could include this option in your version.

jsakalos
8 Mar 2010, 11:08 AM
Thank you for the patch. I'm in the process of porting my extension to Ext 3.x so any contribution is welcome.

heratech
9 Mar 2010, 5:38 PM
I was having the problem of time field overlapping the date field on a form xtabpanel on 3.1.1 (but not 3.1.0).

The width is set to 17px as mentioned above.

When I put deferredRender: true it was ok. However I need deferredRender: false for my form loading (I *think*).

I played around with firebug and it seemed to be caused by:



onResize : function(w, h){
Ext.form.TriggerField.superclass.onResize.call(this, w, h);
var tw = this.getTriggerWidth();
if(Ext.isNumber(w)){
this.el.setWidth(w - tw);
}
this.wrap.setWidth(this.el.getWidth() + tw);
}
this.el.getWidth() was returning 0 (after having 83 set a second before) so the wrap width was being set as 17.



getWidth : function(contentWidth){
var me = this,
dom = me.dom,
hidden = Ext.isIE && me.isStyle('display', 'none'),
w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
return w < 0 ? 0 : w;
}
dom.offsetWidth and dom.clientWidth were both 0.

I used an override to set the width to w for now but would obviously like a proper fix.

Update: http://www.extjs.com/forum/showthread.php?t=88063 < just saw this thread. Will look at in the morning

abraxxa
12 Mar 2010, 12:20 AM
@saki: maybe you can use http://www.extjs.com/deploy/ext-3.2-beta/docs/?class=Ext.form.CompositeField (http://www.extjs.com/forum/../deploy/ext-3.2-beta/docs/?class=Ext.form.CompositeField) internally for rendering. (http://www.extjs.com/forum/../deploy/ext-3.2-beta/docs/?class=Ext.form.CompositeField)

jsakalos
12 Mar 2010, 4:10 AM
I will come in the new version. Of course, then it won't be backwards compatible.

DanielT
22 Mar 2010, 3:02 PM
One issue I've noticed is that 'allowBlank: false' on the field doesn't actually do anything.

jsakalos
22 Mar 2010, 11:04 PM
dataConfig:{allowBlank:false} or timeConfig:{allowBlank:false} or both.

DanielT
23 Mar 2010, 1:34 AM
Thanks Saki, that worked great. Just a note for those of you looking for the same solution, it's dateConfig, not dataConfig. It's obvious enough of a typo, but I figured I'd mention it in case anyone got confused.

jsakalos
23 Mar 2010, 11:24 AM
Yeah, it's a typo, sorry.

Dumas
26 Mar 2010, 10:34 AM
Hi!

When I send the DateTime-Field, i get a strange "T" inside the value, event when I have defined dtSeparator: ' '. The Date-Time-Field sends:
"1970-01-21T00:45:00"

thx
Roland

jsakalos
27 Mar 2010, 8:07 AM
That's ISO format and that's how Ext encodes date. http://en.wikipedia.org/wiki/ISO_8601

Dumas
29 Mar 2010, 7:58 AM
oh, never seens that before.
Thx for the information!

seand
30 Mar 2010, 2:29 PM
jsakalos,

You need to set df and tf's inEditor property to be this.inEditor inside onRender(), otherwise you can't move up/down via the enter key in an editor grid. Basically if inEditor is set to true, it fires the onBlur event for the specific fields 10 ms later than it normally would, which makes all the difference. It took us about 3 hours to track down this issue in our component.



I hate javascript.

jsakalos
30 Mar 2010, 4:05 PM
Thank you for sharing the information.

sramirez
21 Apr 2010, 6:07 AM
I'm trying to use the DateTime Field with a rowEditor. I'm getting the following two errors when clicking the Time combo:

editor.column is undefined

This refers to these lines in the rowEditor extension:



ensureVisible: function(editor){
if(this.isVisible()){this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);}
},


this.list is undefined

This refers to the Combobox section of ext-all.js.

By the way, is anyone else having trouble posting to this forum?

I'm having the same problem. Is there any solution? I can't find it...

Scott Murawski
21 Apr 2010, 8:53 AM
I've had to monkey up the DateTime to work with the roweditor. I'm using Ext 3.2, the 3.2 row editor and "$Id: Ext.ux.form.DateTime.js 813 2010-01-29 23:32:36Z" (currently posted version on the first page).

In the onRender() at the very end of the fn I added:



// NEW: If using DateTime as a row editor, the df needs the column shortcut
// NEW: that the RowEditor put in, so the RowEditor#ensureVisible() works!
if ( this.column ) {
this.df.column = this.column;
this.tf.column = this.column;
}
In beforeDestroy() in the "if(this.isRendered)" check I added:



delete this.df.column; // NEW: In case being used in a RowEditor
delete this.tf.column; // NEW: In case being used in a RowEditor
All of the stuff below this point are all optional updates.

There's some other odd's and ends that I added such as setAllowBlank() function



,setAllowBlank:function(allowBlank){ // NEW
this.df.setAllowBlank(allowBlank);
this.tf.setAllowBlank(allowBlank);
}
EDIT: Previously I thought the usage of timeWidth twice in the date and time was a bug. I read through a ton of pages in this thread and saw it mentioned before which means it's a commenting bug :) I wonder how many people "fixed" the timeWidth/dateWidth "bug" like I did?

In initComponent() in dateConfig, there should be a comment



,width:this.timeWidth // This is not a bug, the dateWidth is calculated in setSize()
I usually end up having validators and needed a way to get the currently set info as some raw data, so I added the getRawValue() function. Note, this is different than getValue(), the getValue() seems to be only updated on blur. The roweditor does a monitorValid and you want to know the value at the exact point in time and not wait for the blur. The getRawValue() just returns the text as all the other Ext field getRawValue() functions.



,getRawValue : function() {
var df_val = this.df.getValue();
var tf_val = this.tf.getValue();

if ( df_val instanceof Date ) {
df_val = df_val.format(this.hiddenDateFormat);
}

if ( df_val && tf_val ) {
return df_val + " " + tf_val;
}

return df_val || tf_val || "";
}
But as you notice, that requires this.hiddenDateFormat. I had to split up the formats so I could individually format the date. Previously there was a "hiddenFormat". You'll notice hiddenFormat by itself is used in a couple of places in the code. I needed to make an access fn for those...



/**
* @cfg {String} hiddenDateFormat Format of date used to store value in hidden field
* and submitted to server (defaults to 'Y-m-d' that is mysql format)
*/
,hiddenDateFormat:'Y-m-d' // NEW
/**
* @cfg {String} hiddenTimeFormat Format of time used to store value in hidden field
* and submitted to server (defaults to 'H:i:s' that is mysql format)
*/
,hiddenTimeFormat:'H:i:s' // NEW
This is the access function which puts the date and time hidden formats together



// NEW - warning, both are required to be set, otherwise it's an empty format!
// NEW - TODO: There probably should be a check requiring them to be set
,getHiddenFormat:function() {
return (this.hiddenDateFormat && this.hiddenTimeFormat)
? this.hiddenDateFormat + " " + this.hiddenTimeFormat
: "";
}
In setValue() I made an update



// OLD
//else if('string' === typeof val && this.hiddenFormat) {
// val = Date.parseDate(val, this.hiddenFormat);
//}

// NEW - the hidden formats should always be specified so just proceed
// NEW - you can put the check back in if you want
else if('string' === typeof val ) {
val = Date.parseDate(val, this.getHiddenFormat()); // NEW: Using getHiddenFormat()
}
I also updated updateHidden() to use the new getHiddenFormat() function



,updateHidden:function() {
if(this.isRendered) { // NEW: Using getHiddenFormat()
var value = this.dateValue instanceof Date ? this.dateValue.format(this.getHiddenFormat()) : '';
this.el.dom.value = value;
}
}
That looks to be about it with my changes. I've been meaning to post these for a while and the last question on the forum helped spur me on :)

EDIT: Also I wanted to mention I have a bug with the validation error qtip messages when I have allowBlank as false. I get "{0} is not a valid time" for the time field. I dunno if it's my bug or not. I'll be needing to fix that in the near future, so I'll post an update when I figure it out.

EDIT: Check out my post about the time validator for Ext 3.2:
http://www.extjs.com/forum/showthread.php?97991-3.2-Ext.form.TimeField-validation-bug&p=462257
I think that was part of the above issue I was having.

jsakalos
21 Apr 2010, 9:41 AM
Thank you very much. I'll incorporate your changes in the next version.

innerbreath
26 Apr 2010, 12:17 PM
Error in IE7. When using the DateTime control with a roweditor in 3.2, I get the following error:

'this.df.el' is null or not an object.

It doesn't happen in FF. Can anyone help?

Also, when using "forceFit: true" on the grouping view of the grid, it does not work well.

Also, when changing the time, it doesn't take unless you navigate to the date first.

Great control though.

realjax
27 Apr 2010, 12:40 AM
Searching through the new forums returns useless results, so if my question was asked before then I'm sorry.

I'm running extjs 3.2 with the date-time plugin. Question is, if I run getValue() on the form that contains an xdatetime filed I get a date back in the form of a String. I would like to get a date object back. Is that possible ?

Scott Murawski
27 Apr 2010, 12:20 PM
Searching through the new forums returns useless results, so if my question was asked before then I'm sorry.

I'm running extjs 3.2 with the date-time plugin. Question is, if I run getValue() on the form that contains an xdatetime filed I get a date back in the form of a String. I would like to get a date object back. Is that possible ?

That doesn't much sense to be because the DateTime code that I have is (perhaps you should verify the datetime code you have matches this)



,getValue:function() {
// create new instance of date
return this.dateValue ? new Date(this.dateValue) : '';
}
If you call the xdatetime form field getValue(), you'll always get a new date object or empty string. I looked through the code and this.dateValue seems to be either an actual date object, undefined or empty string "". If you are using a validator, then the value received though the validator will be a string.

@innerbreath, I'll be needing to use IE6 and 7 and 8 as well, so in the near future if I run into the this.df.el problem I'll need to fix it. Perhaps I'll be testing on IE later in the week. As far as the time/date "taking", that happens upon blur. I had a similar issue which is why above I made a getRawValue() which always gets the current value.

Scott Murawski
27 Apr 2010, 12:29 PM
In the main properties I now have



dateValidator: function () { return true; }
,timeValidator: function () { return true; }


Within the initComponent I updated the validator bits



var self = this;

// create DateField
var dateConfig = Ext.apply({}, {
id:this.id + '-date'
,format:this.dateFormat || Ext.form.DateField.prototype.format
,width:this.timeWidth // Not a bug, dateWidth is calculated in this.setSize()
,selectOnFocus:this.selectOnFocus
,validator: function () { return self.dateValidator.apply(this, arguments) }
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.dateConfig);

// create TimeField
var timeConfig = Ext.apply({}, {
id:this.id + '-time'
,format:this.timeFormat || Ext.form.TimeField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator: function () { return self.timeValidator.apply(this, arguments) }
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.timeConfig);
I wanted to set a validator after it was constructed. The problem I had is upon construction the this.dateValidator is null and the only way to set it would be to use this.df.validator = function () {...}. With this technique, you can change the dateValidator or timeValidator to whatever you want and the current one will always be called. Also your own validator function will be called in the same scope as the original which is the this.df (the actual date field) or this.tf.

This is technique is ever so slightly inefficient, but for me, the convenience outweighs the extra function call.

EDIT: Fixed the previous bugs. The validator functions always need to return true or a message and previously it wasn't returning true which was generating a blank error message.

smartbinary
28 Apr 2010, 7:36 AM
Hello ... I've recently replaced a date editor with the datetime editor. The problem I'm having is that, though the date is displayed properly in the cell, and any datetime selection from the datetime editor does properly update the cell, any existing cell value is not injected into the datetime editor upon cell selection. Instead, I receive a default datetime value of 01/01/1970 00:00:00 in the datetime editor. Here is a snippet from the code:



var cm = new Ext.grid.ColumnModel([{
header: '<span style="font-weight: bold;">#{messages["workflow.task.startDate"]}</span>',
width: 110,
sortable: true,
dataIndex: 'startDateString',
renderer: K12.Workflow.formatDate,
editor: new Ext.ux.form.DateTime({
id: 'startDateString',
name: 'startDateString',
value: 'startDateString',
dateFormat:'m/d/Y',
timePosition: 'below',
dateConfig: {allowBlank: true},
timeFormat:'G:i',
timeConfig: {allowBlank: true}
})
}]);
As you see, I've tried passing the default value used in my grid data index and also by using the same name as that data index, etc. Of course, this is something I'm missing ... would appreciate any pointers!


Regards,

Todd

jsakalos
28 Apr 2010, 7:49 AM
I've never had this problem. Check if the grid store is properly configured, namely if the field type is 'date'.

smartbinary
28 Apr 2010, 9:40 AM
You the man!

taporari
3 May 2010, 11:57 PM
Hi all !
I used Xdatetime for my form. I now want my date field show current date as default value. Hopefully, someone could tell me the way do that.
many thanks

jsakalos
4 May 2010, 1:21 AM
Is this (http://extjs.eu/docs/?class=Ext.ux.form.DateTime&member=emptyToNow) what you want?

taporari
4 May 2010, 10:05 PM
hi Jsakalos !
firstly, many thanks for your reply.
I used that but nothing showed
here is my code(I adjusted emptyToNow:true):


/**
* @class Ext.ux.form.DateTime
* @extends Ext.form.Field
*
* DateTime field, combination of DateField and TimeField
*
* @author Ing. Jozef Sakáloš
* @copyright (c) 2008, Ing. Jozef Sakáloš
* @version 2.0
* @revision $Id: Ext.ux.form.DateTime.js 813 2010-01-29 23:32:36Z jozo $
*
* @license Ext.ux.form.DateTime is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html"
* target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p>
*
* @forum 22661
*
* @donate
* <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
* <input type="hidden" name="cmd" value="_s-xclick">
* <input type="hidden" name="hosted_button_id" value="3430419">
* <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-butcc-donate.gif"
* border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
* <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
* </form>
*/

Ext.ns('Ext.ux.form');

/**
* Creates new DateTime
* @constructor
* @param {Object} config A config object
*/
Ext.ux.form.DateTime = Ext.extend(Ext.form.Field, {
/**
* @cfg {Function} dateValidator A custom validation function to be called during date field
* validation (defaults to null)
*/
dateValidator:null
/**
* @cfg {String/Object} defaultAutoCreate DomHelper element spec
* Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
*/
,defaultAutoCreate:{tag:'input', type:'hidden'}
/**
* @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
*/
,dtSeparator:' '
/**
* @cfg {String} hiddenFormat Format of datetime used to store value in hidden field
* and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql format)
*/
,hiddenFormat:'Y-m-d H:i:s'
/**
* @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
*/
,otherToNow:true
/**
* @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty value.
* If it is true then setValue() sets value of field to current date and time (defaults to false)
*/
,emptyToNow:true
/**
* @cfg {String} timePosition Where the time field should be rendered. 'right' is suitable for forms
* and 'below' is suitable if the field is used as the grid editor (defaults to 'right')
*/
,timePosition:'right' // valid values:'below', 'right'
/**
* @cfg {Function} timeValidator A custom validation function to be called during time field
* validation (defaults to null)
*/
,timeValidator:null
/**
* @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
*/
,timeWidth:100
/**
* @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
*/
,dateFormat:'m/d/y'
/**
* @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
*/
,timeFormat:'g:i A'
/**
* @cfg {Object} dateConfig Config for DateField constructor.
*/
/**
* @cfg {Object} timeConfig Config for TimeField constructor.
*/

// {{{
/**
* @private
* creates DateField and TimeField and installs the necessary event handlers
*/
,initComponent:function() {
// call parent initComponent
Ext.ux.form.DateTime.superclass.initComponent.call(this);

// create DateField
var dateConfig = Ext.apply({}, {
id:this.id + '-date'
,format:this.dateFormat || Ext.form.DateField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.dateValidator
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.dateConfig);
this.df = new Ext.form.DateField(dateConfig);
this.df.ownerCt = this;
delete(this.dateFormat);

// create TimeField
var timeConfig = Ext.apply({}, {
id:this.id + '-time'
,format:this.timeFormat || Ext.form.TimeField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.timeValidator
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.timeConfig);
this.tf = new Ext.form.TimeField(timeConfig);
this.tf.ownerCt = this;
delete(this.timeFormat);

// relay events
this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);

this.on('specialkey', this.onSpecialKey, this);

} // eo function initComponent
// }}}
// {{{
/**
* @private
* Renders underlying DateField and TimeField and provides a workaround for side error icon bug
*/
,onRender:function(ct, position) {
// don't run more than once
if(this.isRendered) {
return;
}

// render underlying hidden field
Ext.ux.form.DateTime.superclass.onRender.call(this, ct, position);

// render DateField and TimeField
// create bounding table
var t;
if('below' === this.timePosition || 'bellow' === this.timePosition) {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[{tag:'td', style:'padding-bottom:1px', cls:'ux-datetime-date'}]}
,{tag:'tr',children:[{tag:'td', cls:'ux-datetime-time'}]}
]}, true);
}
else {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[
{tag:'td',style:'padding-right:4px', cls:'ux-datetime-date'},{tag:'td', cls:'ux-datetime-time'}
]}
]}, true);
}

this.tableEl = t;
this.wrap = t.wrap({cls:'x-form-field-wrap'});
// this.wrap = t.wrap();
this.wrap.on("mousedown", this.onMouseDown, this, {delay:10});

// render DateField & TimeField
this.df.render(t.child('td.ux-datetime-date'));
this.tf.render(t.child('td.ux-datetime-time'));

// workaround for IE trigger misalignment bug
// see http://extjs.com/forum/showthread.php?p=341075#post341075
// if(Ext.isIE && Ext.isStrict) {
// t.select('input').applyStyles({top:0});
// }

this.df.el.swallowEvent(['keydown', 'keypress']);
this.tf.el.swallowEvent(['keydown', 'keypress']);

// create icon for side invalid errorIcon
if('side' === this.msgTarget) {
var elp = this.el.findParent('.x-form-element', 10, true);
if(elp) {
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
}

var o = {
errorIcon:this.errorIcon
,msgTarget:'side'
,alignErrorIcon:this.alignErrorIcon.createDelegate(this)
};
Ext.apply(this.df, o);
Ext.apply(this.tf, o);
// this.df.errorIcon = this.errorIcon;
// this.tf.errorIcon = this.errorIcon;
}

// setup name for submit
this.el.dom.name = this.hiddenName || this.name || this.id;

// prevent helper fields from being submitted
this.df.el.dom.removeAttribute("name");
this.tf.el.dom.removeAttribute("name");

// we're rendered flag
this.isRendered = true;

// update hidden field
this.updateHidden();

} // eo function onRender
// }}}
// {{{
/**
* @private
*/
,adjustSize:Ext.BoxComponent.prototype.adjustSize
// }}}
// {{{
/**
* @private
*/
,alignErrorIcon:function() {
this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
}
// }}}
// {{{
/**
* @private initializes internal dateValue
*/
,initDateValue:function() {
this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
}
// }}}
// {{{
/**
* Calls clearInvalid on the DateField and TimeField
*/
,clearInvalid:function(){
this.df.clearInvalid();
this.tf.clearInvalid();
} // eo function clearInvalid
// }}}
// {{{
/**
* Calls markInvalid on both DateField and TimeField
* @param {String} msg Invalid message to display
*/
,markInvalid:function(msg){
this.df.markInvalid(msg);
this.tf.markInvalid(msg);
} // eo function markInvalid
// }}}
// {{{
/**
* @private
* called from Component::destroy.
* Destroys all elements and removes all listeners we've created.
*/
,beforeDestroy:function() {
if(this.isRendered) {
// this.removeAllListeners();
this.wrap.removeAllListeners();
this.wrap.remove();
this.tableEl.remove();
this.df.destroy();
this.tf.destroy();
}
} // eo function beforeDestroy
// }}}
// {{{
/**
* Disable this component.
* @return {Ext.Component} this
*/
,disable:function() {
if(this.isRendered) {
this.df.disabled = this.disabled;
this.df.onDisable();
this.tf.onDisable();
}
this.disabled = true;
this.df.disabled = true;
this.tf.disabled = true;
this.fireEvent("disable", this);
return this;
} // eo function disable
// }}}
// {{{
/**
* Enable this component.
* @return {Ext.Component} this
*/
,enable:function() {
if(this.rendered){
this.df.onEnable();
this.tf.onEnable();
}
this.disabled = false;
this.df.disabled = false;
this.tf.disabled = false;
this.fireEvent("enable", this);
return this;
} // eo function enable
// }}}
// {{{
/**
* @private Focus date filed
*/
,focus:function() {
this.df.focus();
} // eo function focus
// }}}
// {{{
/**
* @private
*/
,getPositionEl:function() {
return this.wrap;
}
// }}}
// {{{
/**
* @private
*/
,getResizeEl:function() {
return this.wrap;
}
// }}}
// {{{
/**
* @return {Date/String} Returns value of this field
*/
,getValue:function() {
// create new instance of date
return this.dateValue ? new Date(this.dateValue) : '';
} // eo function getValue
// }}}
// {{{
/**
* @return {Boolean} true = valid, false = invalid
* @private Calls isValid methods of underlying DateField and TimeField and returns the result
*/
,isValid:function() {
return this.df.isValid() && this.tf.isValid();
} // eo function isValid
// }}}
// {{{
/**
* Returns true if this component is visible
* @return {boolean}
*/
,isVisible : function(){
return this.df.rendered && this.df.getActionEl().isVisible();
} // eo function isVisible
// }}}
// {{{
/**
* @private Handles blur event
*/
,onBlur:function(f) {
// called by both DateField and TimeField blur events

// revert focus to previous field if clicked in between
if(this.wrapClick) {
f.focus();
this.wrapClick = false;
}

// update underlying value
if(f === this.df) {
this.updateDate();
}
else {
this.updateTime();
}
this.updateHidden();

this.validate();

// fire events later
(function() {
if(!this.df.hasFocus && !this.tf.hasFocus) {
var v = this.getValue();
if(String(v) !== String(this.startValue)) {
this.fireEvent("change", this, v, this.startValue);
}
this.hasFocus = false;
this.fireEvent('blur', this);
}
}).defer(100, this);

} // eo function onBlur
// }}}
// {{{
/**
* @private Handles focus event
*/
,onFocus:function() {
if(!this.hasFocus){
this.hasFocus = true;
this.startValue = this.getValue();
this.fireEvent("focus", this);
}
}
// }}}
// {{{
/**
* @private Just to prevent blur event when clicked in the middle of fields
*/
,onMouseDown:function(e) {
if(!this.disabled) {
this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
}
}
// }}}
// {{{
/**
* @private
* Handles Tab and Shift-Tab events
*/
,onSpecialKey:function(t, e) {
var key = e.getKey();
if(key === e.TAB) {
if(t === this.df && !e.shiftKey) {
e.stopEvent();
this.tf.focus();
}
if(t === this.tf && e.shiftKey) {
e.stopEvent();
this.df.focus();
}
this.updateValue();
}
// otherwise it misbehaves in editor grid
if(key === e.ENTER) {
this.updateValue();
}

} // eo function onSpecialKey
// }}}
// {{{
/**
* Resets the current field value to the originally loaded value
* and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
*/
,reset:function() {
this.df.setValue(this.originalValue);
this.tf.setValue(this.originalValue);
} // eo function reset
// }}}
// {{{
/**
* @private Sets the value of DateField
*/
,setDate:function(date) {
this.df.setValue(date);
} // eo function setDate
// }}}
// {{{
/**
* @private Sets the value of TimeField
*/
,setTime:function(date) {
this.tf.setValue(date);
} // eo function setTime
// }}}
// {{{
/**
* @private
* Sets correct sizes of underlying DateField and TimeField
* With workarounds for IE bugs
*/
,setSize:function(w, h) {
if(!w) {
return;
}
if('below' === this.timePosition) {
this.df.setSize(w, h);
this.tf.setSize(w, h);
if(Ext.isIE) {
this.df.el.up('td').setWidth(w);
this.tf.el.up('td').setWidth(w);
}
}
else {
this.df.setSize(w - this.timeWidth - 4, h);
this.tf.setSize(this.timeWidth, h);

if(Ext.isIE) {
this.df.el.up('td').setWidth(w - this.timeWidth - 4);
this.tf.el.up('td').setWidth(this.timeWidth);
}
}
} // eo function setSize
// }}}
// {{{
/**
* @param {Mixed} val Value to set
* Sets the value of this field
*/
,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat);
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}
this.updateValue();
} // eo function setValue
// }}}
// {{{
/**
* Hide or show this component by boolean
* @return {Ext.Component} this
*/
,setVisible: function(visible){
if(visible) {
this.df.show();
this.tf.show();
}else{
this.df.hide();
this.tf.hide();
}
return this;
} // eo function setVisible
// }}}
//{{{
,show:function() {
return this.setVisible(true);
} // eo function show
//}}}
//{{{
,hide:function() {
return this.setVisible(false);
} // eo function hide
//}}}
// {{{
/**
* @private Updates the date part
*/
,updateDate:function() {

var d = this.df.getValue();
if(d) {
if(!(this.dateValue instanceof Date)) {
this.initDateValue();
if(!this.tf.getValue()) {
this.setTime(this.dateValue);
}
}
this.dateValue.setMonth(0); // because of leap years
this.dateValue.setFullYear(d.getFullYear());
this.dateValue.setMonth(d.getMonth(), d.getDate());
// this.dateValue.setDate(d.getDate());
}
else {
this.dateValue = '';
this.setTime('');
}
} // eo function updateDate
// }}}
// {{{
/**
* @private
* Updates the time part
*/
,updateTime:function() {
var t = this.tf.getValue();
if(t && !(t instanceof Date)) {
t = Date.parseDate(t, this.tf.format);
}
if(t && !this.df.getValue()) {
this.initDateValue();
this.setDate(this.dateValue);
}
if(this.dateValue instanceof Date) {
if(t) {
this.dateValue.setHours(t.getHours());
this.dateValue.setMinutes(t.getMinutes());
this.dateValue.setSeconds(t.getSeconds());
}
else {
this.dateValue.setHours(0);
this.dateValue.setMinutes(0);
this.dateValue.setSeconds(0);
}
}
} // eo function updateTime
// }}}
// {{{
/**
* @private Updates the underlying hidden field value
*/
,updateHidden:function() {
if(this.isRendered) {
var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
this.el.dom.value = value;
}
}
// }}}
// {{{
/**
* @private Updates all of Date, Time and Hidden
*/
,updateValue:function() {

this.updateDate();
this.updateTime();
this.updateHidden();

return;
} // eo function updateValue
// }}}
// {{{
/**
* @return {Boolean} true = valid, false = invalid
* calls validate methods of DateField and TimeField
*/
,validate:function() {
return this.df.validate() && this.tf.validate();
} // eo function validate
// }}}
// {{{
/**
* Returns renderer suitable to render this field
* @param {Object} Column model config
*/
,renderer: function(field) {
var format = field.editor.dateFormat || Ext.ux.form.DateTime.prototype.dateFormat;
format += ' ' + (field.editor.timeFormat || Ext.ux.form.DateTime.prototype.timeFormat);
var renderer = function(val) {
var retval = Ext.util.Format.date(val, format);
return retval;
};
return renderer;
} // eo function renderer
// }}}

}); // eo extend

// register xtype
Ext.reg('xdatetime', Ext.ux.form.DateTime);

// eof
maybe my code is wrong
where can i download this extension Jsakalos?

httpdotcom
19 May 2010, 5:57 AM
not sure if this has been addressed.
Using current code (see post #1) and IE. Setting config "disabled: true" doesn't gray the fieldLabel.

Code adjustment:
,disable:function() {
if(this.isRendered) {
this.df.disabled = this.disabled;
this.df.onDisable();
this.tf.onDisable();
}
this.disabled = true;
this.df.disabled = true;
this.tf.disabled = true;
this.getEl().up('.x-form-item', 10).addClass('x-item-disabled') ;
this.fireEvent("disable", this);
return this;
} // eo function disable


Did the same on enable, using removeClass(). Might not be the cleanest hack, though.

Thanks for excellent code!

ps. neither hide(), show(), or manipulating the hidden configuration deal with the fieldLabel either.

jsakalos
19 May 2010, 10:45 AM
Thank you for the patch.

isit.gd
23 May 2010, 11:57 PM
should this extension be able to set its value when it's inside a direct form - after it's loaded?

Mine just displays a default date on load?

Excelent [plug in btw - cant believe this isn't part of the standard ext!

:)

jsakalos
24 May 2010, 12:40 AM
The extension does not assume any container it may be in. As long as you set a value it should show the value.

burn
26 May 2010, 5:55 AM
Hi Saki,

just reporting a small problem that I found with the extension (which is superb, ty!). If the field is rendered in a form panel which is rendered as a tab in a tabpanel, which in turn is set to "deferredRender: false".. the time field will not align correctly until the panel or its container is manually resized. A quick fix would be to trigger the right event after the doLayout call on the container, but I wouldn't know which one is responsible for the resize and positioning. Ofc if deferred rendering isn't needed it's sufficient not to set that property.

Tested on Ext 3.2.1, FF 3.6.3, windows.

See attachment.

abraxxa
27 May 2010, 12:49 AM
The same happens if the ux is used in a hidden panel after it's expanded.

I've used hideMode:'offsets' as a workaround.

ampnezz
1 Jun 2010, 12:31 AM
Hi! I tried it in google chrome and it doesn't work as expected after clicking date picker.

ampnezz
1 Jun 2010, 12:57 AM
Its working now!
Btw, this made my life easier, Thanks :)

xavierg
2 Jun 2010, 1:15 PM
there is a problem using
format:'W-Y' (week - year)

when I click away the control, it returns to today date

here's the code:



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>


<link rel="stylesheet" type="text/css" href="/data/extjs/resources/css/ext-all.css" />

<script type="text/javascript" src="/data/extjs/adapter/ext/ext-base-debug.js"></script>
<script type="text/javascript" src="/data/extjs/ext-all-debug.js"></script>

<script type="text/javascript">



Ext.onReady(function(){
var df = new Ext.form.DateField({
allowBlank:false,
format:'W-Y',
applyTo: 'fecha_semana'
})
});

</script>


</head>
<body>


<div>
<input id="fecha_semana" type="text" name="fecha_semana" value="" />
</div>
<div>
click here
</div>


</body>
</html>

jsakalos
2 Jun 2010, 1:28 PM
Week and year is not a complete date. Try to find out how Ext itself is handling that. Also, avoid using applyTo unless you exactly know what you are doing.

mystix
10 Jun 2010, 10:02 AM
there is a problem using
format:'W-Y' (week - year)

when I click away the control, it returns to today date


Ext's Date class does not use week numbers to calculate dates.

note: It is impossible to obtain an exact date using just a week number + a year, as the week in question may fall on a boundary between 2 consecutive months, unless you also include the day of the week; since this requirement is extremely rare, week numbers are simply not used in Date class calculations.

erdna
15 Jun 2010, 2:10 AM
Hi jsakalos,

if I switch between DateField and TimeField with TAB or SHIFT-TAB the Event 'blur' was not triggered.
If I switch with mouse it was fine.

erdna
15 Jun 2010, 4:26 AM
Hi jsakalos,

if I switch between DateField and TimeField with TAB or SHIFT-TAB the Event 'blur' was not triggered.
If I switch with mouse it was fine.

This patch work fine for me:


/**
* @private
* Handles Tab and Shift-Tab events
*/
,onSpecialKey:function(t, e) {
var key = e.getKey();
if(key === e.TAB) {
if(t === this.df && !e.shiftKey && !this.tf.disabled) {
e.stopEvent();
this.df.beforeBlur();
this.updateValue();
this.tf.focus();
}
if(t === this.tf && e.shiftKey && !this.df.disabled) {
e.stopEvent();
this.tf.beforeBlur();
this.updateValue();
this.df.focus();
}
}
// otherwise it misbehaves in editor grid
if(key === e.ENTER) {
this.updateValue();
}

} // eo function onSpecialKey

jsakalos
15 Jun 2010, 4:47 AM
Thank you for the patch.

erdna
15 Jun 2010, 5:36 AM
I use the DateTime Field in a CompositeField:

{
xtype: 'compositefield',
fieldLabel: 'Termin',
layout: 'hbox',
anchor: '100%',
items: [{
xtype: 'displayfield',
fieldLabel: 'Label',
value: 'von',
flex: 1
},{
xtype: 'xdatetime',
name: 'appointment_start',
flex: 5
},{
xtype: 'checkbox',
fieldLabel: 'Label',
boxLabel: 'ganzt&auml;gig',
flex: 3,
name: 'full_time',
height: 22,
id: 'full_time',
}]
}

The TAB-Sequence is checkbox, datefield, timefield.
the meaningful sequence is datefield, timefield, checkbox

ideas, how i can correct this?

jsakalos
15 Jun 2010, 5:45 AM
Try to set tabIndex explicitly.

erdna
15 Jun 2010, 8:14 AM
Try to set tabIndex explicitly.

It's not the smart way. I must set the tabIndex-Property on all form elements.

Gerrat
16 Jun 2010, 7:28 AM
The time field displays the correct initial time, but doesn't appear to update unless the minute happens to fall on a 15 minute boundary.

eg. it will show 2:18 PM, but you can't change it to 2:23 PM (but 2:15 PM and 2:30 PM would trigger the change). Is there a way to get this to work?

Thanks.

jsakalos
16 Jun 2010, 8:30 AM
I've never had this problem. Post your configuration or, even better, a showcase.

Gerrat
16 Jun 2010, 10:33 AM
Thanks for your consideration, Saki.

Sorry, I don't have an externally facing site I can showcase this at. I've tried to dummy up an example though tweaking the Editor Grid from (http://www.sencha.com/deploy/dev/examples/grid/edit-grid.html). See attachment and code below.

You may need to tweak the extjs paths below, and rename the attached plants.txt to plants.xml (I just added some times to the date field in the file).

BTW, I'm using the Extjs 3.2.1 if it matters.

One other thing I noticed is that if the dateFormat for the DateTime field in the reader does not match the dateFormat and timeFormat for the DateTime editor; as soon as a field is clicked on in the grid, it is marked dirty - even when nothing is changed.

Regards.



<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="extjs/resources/css/ext-all.css">
<script type="text/javascript" src="extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="extjs/ext-all-debug.js"></script>
<link rel="stylesheet" type="text/css" href="extjs/examples/grid/examples.css" />
<script type="text/javascript">


Ext.ns('Ext.ux.form');

/**
* Creates new DateTime
* @constructor
* @param {Object} config A config object
*/
Ext.ux.form.DateTime = Ext.extend(Ext.form.Field, {
/**
* @cfg {Function} dateValidator A custom validation function to be called during date field
* validation (defaults to null)
*/
dateValidator:null
/**
* @cfg {String/Object} defaultAutoCreate DomHelper element spec
* Let superclass to create hidden field instead of textbox. Hidden will be submittend to server
*/
,defaultAutoCreate:{tag:'input', type:'hidden'}
/**
* @cfg {String} dtSeparator Date - Time separator. Used to split date and time (defaults to ' ' (space))
*/
,dtSeparator:' '
/**
* @cfg {String} hiddenFormat Format of datetime used to store value in hidden field
* and submitted to server (defaults to 'Y-m-d H:i:s' that is mysql format)
*/
,hiddenFormat:'Y-m-d H:i:s'
/**
* @cfg {Boolean} otherToNow Set other field to now() if not explicly filled in (defaults to true)
*/
,otherToNow:true
/**
* @cfg {Boolean} emptyToNow Set field value to now on attempt to set empty value.
* If it is true then setValue() sets value of field to current date and time (defaults to false)
*/
/**
* @cfg {String} timePosition Where the time field should be rendered. 'right' is suitable for forms
* and 'below' is suitable if the field is used as the grid editor (defaults to 'right')
*/
,timePosition:'right' // valid values:'below', 'right'
/**
* @cfg {Function} timeValidator A custom validation function to be called during time field
* validation (defaults to null)
*/
,timeValidator:null
/**
* @cfg {Number} timeWidth Width of time field in pixels (defaults to 100)
*/
,timeWidth:100
/**
* @cfg {String} dateFormat Format of DateField. Can be localized. (defaults to 'm/y/d')
*/
,dateFormat:'m/d/y'
/**
* @cfg {String} timeFormat Format of TimeField. Can be localized. (defaults to 'g:i A')
*/
,timeFormat:'g:i A'
/**
* @cfg {Object} dateConfig Config for DateField constructor.
*/
/**
* @cfg {Object} timeConfig Config for TimeField constructor.
*/

// {{{
/**
* @private
* creates DateField and TimeField and installs the necessary event handlers
*/
,initComponent:function() {
// call parent initComponent
Ext.ux.form.DateTime.superclass.initComponent.call(this);

// create DateField
var dateConfig = Ext.apply({}, {
id:this.id + '-date'
,format:this.dateFormat || Ext.form.DateField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.dateValidator
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.dateConfig);
this.df = new Ext.form.DateField(dateConfig);
this.df.ownerCt = this;
delete(this.dateFormat);

// create TimeField
var timeConfig = Ext.apply({}, {
id:this.id + '-time'
,format:this.timeFormat || Ext.form.TimeField.prototype.format
,width:this.timeWidth
,selectOnFocus:this.selectOnFocus
,validator:this.timeValidator
,listeners:{
blur:{scope:this, fn:this.onBlur}
,focus:{scope:this, fn:this.onFocus}
}
}, this.timeConfig);
this.tf = new Ext.form.TimeField(timeConfig);
this.tf.ownerCt = this;
delete(this.timeFormat);

// relay events
this.relayEvents(this.df, ['focus', 'specialkey', 'invalid', 'valid']);
this.relayEvents(this.tf, ['focus', 'specialkey', 'invalid', 'valid']);

this.on('specialkey', this.onSpecialKey, this);

} // eo function initComponent
// }}}
// {{{
/**
* @private
* Renders underlying DateField and TimeField and provides a workaround for side error icon bug
*/
,onRender:function(ct, position) {
// don't run more than once
if(this.isRendered) {
return;
}

// render underlying hidden field
Ext.ux.form.DateTime.superclass.onRender.call(this, ct, position);

// render DateField and TimeField
// create bounding table
var t;
if('below' === this.timePosition || 'bellow' === this.timePosition) {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[{tag:'td', style:'padding-bottom:1px', cls:'ux-datetime-date'}]}
,{tag:'tr',children:[{tag:'td', cls:'ux-datetime-time'}]}
]}, true);
}
else {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[
{tag:'td',style:'padding-right:4px', cls:'ux-datetime-date'},{tag:'td', cls:'ux-datetime-time'}
]}
]}, true);
}

this.tableEl = t;
this.wrap = t.wrap({cls:'x-form-field-wrap'});
// this.wrap = t.wrap();
this.wrap.on("mousedown", this.onMouseDown, this, {delay:10});

// render DateField & TimeField
this.df.render(t.child('td.ux-datetime-date'));
this.tf.render(t.child('td.ux-datetime-time'));

// workaround for IE trigger misalignment bug
// see http://extjs.com/forum/showthread.php?p=341075#post341075
// if(Ext.isIE && Ext.isStrict) {
// t.select('input').applyStyles({top:0});
// }

this.df.el.swallowEvent(['keydown', 'keypress']);
this.tf.el.swallowEvent(['keydown', 'keypress']);

// create icon for side invalid errorIcon
if('side' === this.msgTarget) {
var elp = this.el.findParent('.x-form-element', 10, true);
if(elp) {
this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
}

var o = {
errorIcon:this.errorIcon
,msgTarget:'side'
,alignErrorIcon:this.alignErrorIcon.createDelegate(this)
};
Ext.apply(this.df, o);
Ext.apply(this.tf, o);
// this.df.errorIcon = this.errorIcon;
// this.tf.errorIcon = this.errorIcon;
}

// setup name for submit
this.el.dom.name = this.hiddenName || this.name || this.id;

// prevent helper fields from being submitted
this.df.el.dom.removeAttribute("name");
this.tf.el.dom.removeAttribute("name");

// we're rendered flag
this.isRendered = true;

// update hidden field
this.updateHidden();

} // eo function onRender
// }}}
// {{{
/**
* @private
*/
,adjustSize:Ext.BoxComponent.prototype.adjustSize
// }}}
// {{{
/**
* @private
*/
,alignErrorIcon:function() {
this.errorIcon.alignTo(this.tableEl, 'tl-tr', [2, 0]);
}
// }}}
// {{{
/**
* @private initializes internal dateValue
*/
,initDateValue:function() {
this.dateValue = this.otherToNow ? new Date() : new Date(1970, 0, 1, 0, 0, 0);
}
// }}}
// {{{
/**
* Calls clearInvalid on the DateField and TimeField
*/
,clearInvalid:function(){
this.df.clearInvalid();
this.tf.clearInvalid();
} // eo function clearInvalid
// }}}
// {{{
/**
* Calls markInvalid on both DateField and TimeField
* @param {String} msg Invalid message to display
*/
,markInvalid:function(msg){
this.df.markInvalid(msg);
this.tf.markInvalid(msg);
} // eo function markInvalid
// }}}
// {{{
/**
* @private
* called from Component::destroy.
* Destroys all elements and removes all listeners we've created.
*/
,beforeDestroy:function() {
if(this.isRendered) {
// this.removeAllListeners();
this.wrap.removeAllListeners();
this.wrap.remove();
this.tableEl.remove();
this.df.destroy();
this.tf.destroy();
}
} // eo function beforeDestroy
// }}}
// {{{
/**
* Disable this component.
* @return {Ext.Component} this
*/
,disable:function() {
if(this.isRendered) {
this.df.disabled = this.disabled;
this.df.onDisable();
this.tf.onDisable();
}
this.disabled = true;
this.df.disabled = true;
this.tf.disabled = true;
this.fireEvent("disable", this);
return this;
} // eo function disable
// }}}
// {{{
/**
* Enable this component.
* @return {Ext.Component} this
*/
,enable:function() {
if(this.rendered){
this.df.onEnable();
this.tf.onEnable();
}
this.disabled = false;
this.df.disabled = false;
this.tf.disabled = false;
this.fireEvent("enable", this);
return this;
} // eo function enable
// }}}
// {{{
/**
* @private Focus date filed
*/
,focus:function() {
this.df.focus();
} // eo function focus
// }}}
// {{{
/**
* @private
*/
,getPositionEl:function() {
return this.wrap;
}
// }}}
// {{{
/**
* @private
*/
,getResizeEl:function() {
return this.wrap;
}
// }}}
// {{{
/**
* @return {Date/String} Returns value of this field
*/
,getValue:function() {
// create new instance of date
return this.dateValue ? new Date(this.dateValue) : '';
} // eo function getValue
// }}}
// {{{
/**
* @return {Boolean} true = valid, false = invalid
* @private Calls isValid methods of underlying DateField and TimeField and returns the result
*/
,isValid:function() {
return this.df.isValid() && this.tf.isValid();
} // eo function isValid
// }}}
// {{{
/**
* Returns true if this component is visible
* @return {boolean}
*/
,isVisible : function(){
return this.df.rendered && this.df.getActionEl().isVisible();
} // eo function isVisible
// }}}
// {{{
/**
* @private Handles blur event
*/
,onBlur:function(f) {
// called by both DateField and TimeField blur events

// revert focus to previous field if clicked in between
if(this.wrapClick) {
f.focus();
this.wrapClick = false;
}

// update underlying value
if(f === this.df) {
this.updateDate();
}
else {
this.updateTime();
}
this.updateHidden();

this.validate();

// fire events later
(function() {
if(!this.df.hasFocus && !this.tf.hasFocus) {
var v = this.getValue();
if(String(v) !== String(this.startValue)) {
this.fireEvent("change", this, v, this.startValue);
}
this.hasFocus = false;
this.fireEvent('blur', this);
}
}).defer(100, this);

} // eo function onBlur
// }}}
// {{{
/**
* @private Handles focus event
*/
,onFocus:function() {
if(!this.hasFocus){
this.hasFocus = true;
this.startValue = this.getValue();
this.fireEvent("focus", this);
}
}
// }}}
// {{{
/**
* @private Just to prevent blur event when clicked in the middle of fields
*/
,onMouseDown:function(e) {
if(!this.disabled) {
this.wrapClick = 'td' === e.target.nodeName.toLowerCase();
}
}
// }}}
// {{{
/**
* @private
* Handles Tab and Shift-Tab events
*/
,onSpecialKey:function(t, e) {
var key = e.getKey();
if(key === e.TAB) {
if(t === this.df && !e.shiftKey) {
e.stopEvent();
this.tf.focus();
}
if(t === this.tf && e.shiftKey) {
e.stopEvent();
this.df.focus();
}
this.updateValue();
}
// otherwise it misbehaves in editor grid
if(key === e.ENTER) {
this.updateValue();
}

} // eo function onSpecialKey
// }}}
// {{{
/**
* Resets the current field value to the originally loaded value
* and clears any validation messages. See Ext.form.BasicForm.trackResetOnLoad
*/
,reset:function() {
this.df.setValue(this.originalValue);
this.tf.setValue(this.originalValue);
} // eo function reset
// }}}
// {{{
/**
* @private Sets the value of DateField
*/
,setDate:function(date) {
this.df.setValue(date);
} // eo function setDate
// }}}
// {{{
/**
* @private Sets the value of TimeField
*/
,setTime:function(date) {
this.tf.setValue(date);
} // eo function setTime
// }}}
// {{{
/**
* @private
* Sets correct sizes of underlying DateField and TimeField
* With workarounds for IE bugs
*/
,setSize:function(w, h) {
if(!w) {
return;
}
if('below' === this.timePosition) {
this.df.setSize(w, h);
this.tf.setSize(w, h);
if(Ext.isIE) {
this.df.el.up('td').setWidth(w);
this.tf.el.up('td').setWidth(w);
}
}
else {
this.df.setSize(w - this.timeWidth - 4, h);
this.tf.setSize(this.timeWidth, h);

if(Ext.isIE) {
this.df.el.up('td').setWidth(w - this.timeWidth - 4);
this.tf.el.up('td').setWidth(this.timeWidth);
}
}
} // eo function setSize
// }}}
// {{{
/**
* @param {Mixed} val Value to set
* Sets the value of this field
*/
,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
val = Date.parseDate(val, this.hiddenFormat);
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}
this.updateValue();
} // eo function setValue
// }}}
// {{{
/**
* Hide or show this component by boolean
* @return {Ext.Component} this
*/
,setVisible: function(visible){
if(visible) {
this.df.show();
this.tf.show();
}else{
this.df.hide();
this.tf.hide();
}
return this;
} // eo function setVisible
// }}}
//{{{
,show:function() {
return this.setVisible(true);
} // eo function show
//}}}
//{{{
,hide:function() {
return this.setVisible(false);
} // eo function hide
//}}}
// {{{
/**
* @private Updates the date part
*/
,updateDate:function() {

var d = this.df.getValue();
if(d) {
if(!(this.dateValue instanceof Date)) {
this.initDateValue();
if(!this.tf.getValue()) {
this.setTime(this.dateValue);
}
}
this.dateValue.setMonth(0); // because of leap years
this.dateValue.setFullYear(d.getFullYear());
this.dateValue.setMonth(d.getMonth(), d.getDate());
// this.dateValue.setDate(d.getDate());
}
else {
this.dateValue = '';
this.setTime('');
}
} // eo function updateDate
// }}}
// {{{
/**
* @private
* Updates the time part
*/
,updateTime:function() {
var t = this.tf.getValue();
if(t && !(t instanceof Date)) {
t = Date.parseDate(t, this.tf.format);
}
if(t && !this.df.getValue()) {
this.initDateValue();
this.setDate(this.dateValue);
}
if(this.dateValue instanceof Date) {
if(t) {
this.dateValue.setHours(t.getHours());
this.dateValue.setMinutes(t.getMinutes());
this.dateValue.setSeconds(t.getSeconds());
}
else {
this.dateValue.setHours(0);
this.dateValue.setMinutes(0);
this.dateValue.setSeconds(0);
}
}
} // eo function updateTime
// }}}
// {{{
/**
* @private Updates the underlying hidden field value
*/
,updateHidden:function() {
if(this.isRendered) {
var value = this.dateValue instanceof Date ? this.dateValue.format(this.hiddenFormat) : '';
this.el.dom.value = value;
}
}
// }}}
// {{{
/**
* @private Updates all of Date, Time and Hidden
*/
,updateValue:function() {

this.updateDate();
this.updateTime();
this.updateHidden();

return;
} // eo function updateValue
// }}}
// {{{
/**
* @return {Boolean} true = valid, false = invalid
* calls validate methods of DateField and TimeField
*/
,validate:function() {
return this.df.validate() && this.tf.validate();
} // eo function validate
// }}}
// {{{
/**
* Returns renderer suitable to render this field
* @param {Object} Column model config
*/
,renderer: function(field) {
var format = field.editor.dateFormat || Ext.ux.form.DateTime.prototype.dateFormat;
format += ' ' + (field.editor.timeFormat || Ext.ux.form.DateTime.prototype.timeFormat);
var renderer = function(val) {
var retval = Ext.util.Format.date(val, format);
return retval;
};
return renderer;
} // eo function renderer
// }}}

}); // eo extend

// register xtype
Ext.reg('xdatetime', Ext.ux.form.DateTime);

// eof

Ext.ns('Ext.ux.grid');
Ext.ux.grid.CheckColumn = function(config){
Ext.apply(this, config);
if(!this.id){
this.id = Ext.id();
}
this.renderer = this.renderer.createDelegate(this);
};

Ext.ux.grid.CheckColumn.prototype ={
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

onMouseDown : function(e, t){
if(Ext.fly(t).hasClass(this.createId())){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
},

renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return String.format('<div class="x-grid3-check-col{0} {1}"> </div>', v ? '-on' : '', this.createId());
},

createId : function(){
return 'x-grid3-cc-' + this.id;
}
};

// register ptype
Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn);

// backwards compat
Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn;


//Ext.BLANK_IMAGE_URL = '../ext/resources/images/default/s.gif';
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/s.gif';
Ext.onReady(function() {
Ext.QuickTips.init();
Ext.form.Field.prototype.msgTarget = 'side';

function formatDate(value){
return value ? value.dateFormat('M d, Y g:i a') : '';
}

// shorthand alias
var fm = Ext.form;

// the check column is created using a custom plugin
var checkColumn = new Ext.grid.CheckColumn({
header: 'Indoor?',
dataIndex: 'indoor',
width: 55
});

// the column model has information about grid columns
// dataIndex maps the column to the specific data field in
// the data store (created below)
var cm = new Ext.grid.ColumnModel({
// specify any defaults for each column
defaults: {
sortable: true // columns are not sortable by default
},
columns: [
{
id: 'common',
header: 'Common Name',
dataIndex: 'common',
width: 220,
// use shorthand alias defined above
editor: new fm.TextField({
allowBlank: false
})
}, {
header: 'Light',
dataIndex: 'light',
width: 130,
editor: new fm.ComboBox({
typeAhead: true,
triggerAction: 'all',
// transform the data already specified in html
transform: 'light',
lazyRender: true,
listClass: 'x-combo-list-small'
})
}, {
header: 'Price',
dataIndex: 'price',
width: 70,
align: 'right',
renderer: 'usMoney',
editor: new fm.NumberField({
allowBlank: false,
allowNegative: false,
maxValue: 100000
})
}, {
header: 'Available',
dataIndex: 'availDate',
width: 195,
renderer: formatDate,
editor: new Ext.ux.form.DateTime({
dateFormat: 'm/d/Y',
timeFormat: 'H:i'
})
},
checkColumn // the plugin instance
]
});

// create the Data Store
var store = new Ext.data.Store({
// destroy the store if the grid is destroyed
autoDestroy: true,

// load remote data using HTTP
url: 'xml/plants.xml',

// specify a XmlReader (coincides with the XML format of the returned data)
reader: new Ext.data.XmlReader({
// records will have a 'plant' tag
record: 'plant',
// use an Array of field definition objects to implicitly create a Record constructor
fields: [
// the 'name' below matches the tag name to read, except 'availDate'
// which is mapped to the tag 'availability'
{name: 'common', type: 'string'},
{name: 'botanical', type: 'string'},
{name: 'light'},
{name: 'price', type: 'float'},
// dates can be automatically converted by specifying dateFormat
{name: 'availDate', mapping: 'availability', type: 'date', dateFormat: 'm/d/Y H:i'},
{name: 'indoor', type: 'bool'}
]
}),

sortInfo: {field:'common', direction:'ASC'}
});

// create the editor grid
var grid = new Ext.grid.EditorGridPanel({
store: store,
cm: cm,
renderTo: 'editor-grid',
width: 600,
height: 300,
autoExpandColumn: 'common', // column with this id will be expanded
title: 'Edit Plants?',
frame: true,
// specify the check column plugin on the grid so the plugin is initialized
plugins: checkColumn,
clicksToEdit: 1,
tbar: [{
text: 'Add Plant',
handler : function(){
// access the Record constructor through the grid's store
var Plant = grid.getStore().recordType;
var p = new Plant({
common: 'New Plant 1',
light: 'Mostly Shade',
price: 0,
availDate: (new Date()).clearTime(),
indoor: false
});
grid.stopEditing();
store.insert(0, p);
grid.startEditing(0, 0);
}
}]
});

// manually trigger the data store load
store.load({
// store loading is asynchronous, use a load listener or callback to handle results
callback: function(){
Ext.Msg.show({
title: 'Store Load Callback',
msg: 'store was loaded, data available for processing',
modal: false,
icon: Ext.Msg.INFO,
buttons: Ext.Msg.OK
});
}
});
});
</script>
<title>Ext.ux.form.DateTime Example (in editor grid)</title>
</head>
<body>
<h1>Editor Grid Example with DateTime</h1>

<select name="light" id="light" style="display: none;">
<option value="Shade">Shade</option>
<option value="Mostly Shady">Mostly Shady</option>
<option value="Sun or Shade">Sun or Shade</option>

<option value="Mostly Sunny">Mostly Sunny</option>
<option value="Sunny">Sunny</option>
</select>

<div id="editor-grid"></div>
</body>
</html>

lakshmi
21 Jun 2010, 4:57 AM
Hi..

Really useful extension. But I've a scenario to restrict the date time values to current date and time. Currently it is supporting but the time values are not generating accordingly. ie, if the current time is 4:54 PM, I am expecting to get time combo containing only values after 4:54PM. How can I implement this. Please help me.

jsakalos
21 Jun 2010, 5:09 AM
You could achieve it by setting minValue on TimeField. (Untested, just theory).

lakshmi
21 Jun 2010, 5:13 AM
Yes..Im using minValue. But the problem is timefield will get selected with that current time and on expand it will show all other values and it is possible to select them too.

jsakalos
21 Jun 2010, 5:42 AM
Where do you put minValue? Should go to timeConfig, not to DateTime directly.

lakshmi
21 Jun 2010, 5:52 AM
yes I'm using timeconfig. But for minValue I used new Date(), it will set the timefield with current time but the field becomes un-expandable. My code snippet follows: please suggest me a solution.

{
xtype: 'xdatetime',
fieldLabel: 'Wanted Date',
id: 'job_WantedDate',
name: 'job[WantedDate]',
allowBlank: true,
dateConfig:{
minValue:new Date()
},
timeConfig:{
minValue:new Date() // this will select the current time but not able to expand timefield
}
}

jsakalos
21 Jun 2010, 6:06 AM
Take a look at original TimeField docs - I do nothing special with it so the behavior of the default field applies also here. The code above looks good but I'm not sure how it affects the dropdown. Docs may have an answer.

lakshmi
21 Jun 2010, 6:13 AM
Ok....thanks a lot for your quick reply....if I get any solution I'll surely get back to you...Also many thanks for this wonderful extension.

qlegrand
5 Jul 2010, 10:58 PM
Saki,

I've found your extension really useful but it doesn't seem possible to configure a separate label for the time field. This would look neat and not consume space when timePosition = 'below'.

Would it be possible to add that capability, perhaps using fieldLabel property in the timeConfig?

Regards,
qlegrand

jsakalos
6 Jul 2010, 12:55 AM
Well, everything is possible, however, I do not think that giving the time a label is very useful. Date and DateTime fields both define "a point in timeline", only with different accuracy. So label should say what kind of that "point in timeline" is, e.g. "Meeting starts at" but not to give one label to Date and another to Time - they are both the same thing.

Scott Murawski
6 Jul 2010, 11:37 AM
Saki,

I've found your extension really useful but it doesn't seem possible to configure a separate label for the time field. This would look neat and not consume space when timePosition = 'below'.

Would it be possible to add that capability, perhaps using fieldLabel property in the timeConfig?

Regards,
qlegrand

Hi, I've used a "separate label" for the time field by doing somewhat of a silly hack



"fieldLabel" : "<div style='float:left;width:130'>Start Date:</div><div style='float:left;margin-right:-3px'>Start Time</div>",


The reason why I used the div was to get the Start Date and Start Time individual widths. This usage is NOT friendly for the width because it's hardcoded in there and if you change the width of the actual date field then the labels will be misaligned. At least you can get by for the time being though :)

qlegrand
6 Jul 2010, 6:07 PM
Thanks Scott - your hack doesn't do what I want because I have the date part immediately above the time part (timePosition: 'below'). I was trying to avoid having to mess with the HTML & CSS myself but it looks like I have to anyway.

Regards,
Quentin Le Grand

qlegrand
6 Jul 2010, 6:13 PM
Hi Saki,

Thanks for your response. I agree that it really doesn't add much value but the main reason I would like that capability is to simplify my documentation (so I can more easily reference the separate fields)!

Now there's a surprise!

Regards,
Quentin Le Grand.
(probably one of the few Javascript/ PHP programmers that make you look young)

Saki,

I've managed to achieve what I wanted by providing a new config property for the Datetime field called 'labelMethod', which, if set to 'cell', will cause the labels for both date and time to be taken from 'dateConfig' and 'timeConfig' respectively and rendered as table cells (rather than in the 'label' tag which I avoid by setting 'hideLabel' true)

If you're interested in the changes I've done to the Datetime.js, I'll send them to you so you can fix them to your standards.

Regards,
Quentin Le Grand (qlegrand@gmail.com).

qlegrand
11 Jul 2010, 3:29 PM
Hi Scott,

I've achieved what I wanted by modifying the extension to generate the labels as table cells - I use a new config property 'labelMethod' = 'cell' to use this method. Here are the changes if you're interested:

Ext.ux.form.DateTime.superclass.initComponent.call(this);

// If the labelMethod property = 'cell', the labels are generated as table cells
// so 'hideLabel' is set true so avoiding the usual method by which labels are
// generated using the 'label' tag
if ( this.labelMethod == 'cell' ) {
this.hideLabel = true;
this.dateLabelWidth = this.dateConfig.labelWidth;
this.dateLabel = this.dateConfig.fieldLabel || this.fieldLabel || '' + this.labelSeparator;
this.timeLabelWidth = this.timeConfig.labelWidth || 0;
this.timeLabel = this.timeConfig.fieldLabel || '' + this.labelSeparator;
}
.....
.....
and ....

// If the labelMethod property = 'cell', the labels are generated as table cells
var dateLabelStyle = 'padding: 3px 5px 3px 0; width: ' + (this.dateLabelWidth || 0) + 'px;';
var timeLabelStyle = 'padding: 3px 5px 3px 0; width: ' + (this.timeLabelWidth || 0) + 'px;';
if ('below' === this.timePosition || 'bellow' === this.timePosition) {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[
this.labelMethod == 'cell' ? {tag:'td', style:dateLabelStyle, html: this.dateConfig.fieldLabel} : {}
,{tag:'td', style:'padding-bottom:1px', cls:'ux-datetime-date'}
]
}
,{tag:'tr',children:[
this.labelMethod == 'cell' ? {tag:'td', style:timeLabelStyle, html: this.timeConfig.fieldLabel} : {}
,{tag:'td', cls:'ux-datetime-time'}
]
}
]}, true);
}
else {
t = Ext.DomHelper.append(ct, {tag:'table',style:'border-collapse:collapse',children:[
{tag:'tr',children:[
this.labelMethod == 'cell' ? {tag:'td', style:dateLabelStyle, html: this.dateConfig.fieldLabel} : {}
,{tag:'td',style:'padding-right:4px', cls:'ux-datetime-date'}
,this.labelMethod == 'cell' ? {tag:'td', style:timeLabelStyle, html: this.timeConfig.fieldLabel} : {}
,{tag:'td', cls:'ux-datetime-time'}
]}
]}, true);
}
....
....

Here's how I configure it:
...
labelMethod: 'cell',
dateConfig: {
altFormats: 'd-m-Y|Y-n-d',
allowBlank: true,
emptyText: 'yyyy-mm-dd',
vtype: 'daterange',
labelWidth: 160, // used for labelMethod 'cell' (see above)
fieldLabel: 'Start Date:' // used for labelMethod 'cell' (see above)
},
timeConfig: {
altFormats: 'H:i:s',
allowBlank: true,
labelWidth: 160, // used for labelMethod 'cell' (see above)
fieldLabel: 'Start Time:' // used for labelMethod 'cell' (see above)
}
...
...

I'm sure this could be made more elegant and that a more appropriate property name could be used but it works well and the default behaviour is unchanged.

One thing I haven't bothered with is if the labelSeparator is inherited from up the hierarchy - I simply add it to my own label.

Regards,
Quentin Le Grand.

jsakalos
12 Jul 2010, 12:40 AM
Thank you for the code Quentin.

sean.zhou
22 Jul 2010, 3:07 PM
Hi Jozef,

Thank you for making this widget. I want to use it in such a way that the user can only set the time to a date/time field. Here is what I have done:


{
xtype: 'xdatetime'
,timeFormat: 'H:i:s'
,dateConfig: {hidden: true}
,timePosition: 'bellow'
}

It functioned correctly except that back tabbing from the time field (shift-tab while the time field is in focus) did not bring focus to the previous field. This was using version 2, 2010-01-29 23:32:36Z of your code.

Regards,

abraxxa
23 Jul 2010, 3:14 AM
Why do you not use a timefield if only time is what you want?

sean.zhou
23 Jul 2010, 9:25 AM
I need a date time field to keep the date value, except the date portion is assigned by other part of the program.

httpdotcom
23 Jul 2010, 9:37 AM
I need a date time field to keep the date value, except the date portion is assigned by other part of the program.

Use a composite field set to readOnly: true, and write to it from the other process.

lwarring
29 Jul 2010, 7:36 AM
Hi, I'm using the DateTime field in an EditorGridPanel, and I'm having trouble setting a default time value. Here is my grid column config - in the timeConfig I am using the value attribute to default the time to '6:00'. Any ideas?
Thanks!



{ header: 'Start Date',
width: 110,
sortable: true,
dataIndex: 'startDateString',
renderer: myFormatDate,
editor: new Ext.ux.form.DateTime(
{ dateFormat:'m/d/Y',
timePosition: 'below',
dateConfig: { allowBlank: true},
timeFormat:'G:i',
timeConfig: { allowBlank: true, value: '6:00', triggerAction: 'all' },
otherToNow: false
})
}

jsakalos
29 Jul 2010, 1:37 PM
I'm having trouble setting a default time value

What shows instead of 6:00? 'Cause your config seems to be correct. Or try 06:00.

ajmas
4 Aug 2010, 9:20 AM
I was using the code at the beginning of the topic (I assume it is the one you have been keeping updated), and ran into a small issue, with regards to parsing the initial date-time value:

I am using it in an EditorGridPanel, with data populated using a JSONStore. In certain cases the date-time value comes back with only the date value. When this happens and I go to the edit the value I find myself with a date of 1 Jan 1970. Looking at the code the issue is in the setValue() method. Basically the logic in

else if('string' === typeof val && this.hiddenFormat)

block will cause val to be null and thus cause the val to be redefined in the test case following it:

val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);

this means the following 'if-else' block will never enter the else section, which is able to handle date only values. I change the code thus:



,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
var oldVal = val;
val = Date.parseDate(val, this.hiddenFormat);
// INFO if val is null use the original value, since we will try below
if ( !val ) {
val = oldVal;
}
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}
this.updateValue();
} // eo function setValue


The modified code is following the "INFO" comment.

Dumas
6 Aug 2010, 7:57 AM
Hi!

I have a datetime field with this config:

{
xtype:'xdatetime',
name: 'till',
fieldLabel: 'End date',
hiddenFormat:'d.m.Y H:i', // that's how the components reads the date if it's not a date object
timeFormat:'H:i',
timeConfig: {
altFormats:'H:i',
allowBlank:false
},
dateFormat:'d.m.Y',
dateConfig: {
altFormats:'d.n.Y|d.m.Y',
allowBlank:false
}

When I load the field with a date like "30.09.2010 13:12" it is displayed right, but it sends exactly "30.09.2010 13:12" to the server. When I change the date it is send like this: "30.09.2010T13:12"

What do I have to do so everything is send in the same format?

thx
Dumas

Lobo
16 Aug 2010, 8:05 AM
Hi Saki,

Sorry if this has been discussed before in this long thread ...

I am using your DateTime ux on ExtJs 3.2.1, but I'm having problems with the 'reset' functionality.

When loading a form using 'trackResetOnLoad', if I change the ux original loaded value it works ok, but if I execute 'reset' in the form, the ux does not set the correct value internally (even though it shows the correct values in the composite fields), and therefore it does not send the correct value to the server.

I believe this could be resolved as follows:



,reset:function() {
// this.df.setValue(this.originalValue);
// this.tf.setValue(this.originalValue);
this.setValue(this.originalValue);
}


Unless I'm doing something wrong or you recommend otherwise?.

Best regards,

Lobo

NoahK17
18 Aug 2010, 9:29 AM
I was using the code at the beginning of the topic (I assume it is the one you have been keeping updated), and ran into a small issue, with regards to parsing the initial date-time value:

I am using it in an EditorGridPanel, with data populated using a JSONStore. In certain cases the date-time value comes back with only the date value. When this happens and I go to the edit the value I find myself with a date of 1 Jan 1970. Looking at the code the issue is in the setValue() method. Basically the logic in

else if('string' === typeof val && this.hiddenFormat)

block will cause val to be null and thus cause the val to be redefined in the test case following it:

val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);

this means the following 'if-else' block will never enter the else section, which is able to handle date only values. I change the code thus:



,setValue:function(val) {
if(!val && true === this.emptyToNow) {
this.setValue(new Date());
return;
}
else if(!val) {
this.setDate('');
this.setTime('');
this.updateValue();
return;
}
if ('number' === typeof val) {
val = new Date(val);
}
else if('string' === typeof val && this.hiddenFormat) {
var oldVal = val;
val = Date.parseDate(val, this.hiddenFormat);
// INFO if val is null use the original value, since we will try below
if ( !val ) {
val = oldVal;
}
}
val = val ? val : new Date(1970, 0 ,1, 0, 0, 0);
var da;
if(val instanceof Date) {
this.setDate(val);
this.setTime(val);
this.dateValue = new Date(Ext.isIE ? val.getTime() : val);
}
else {
da = val.split(this.dtSeparator);
this.setDate(da[0]);
if(da[1]) {
if(da[2]) {
// add am/pm part back to time
da[1] += da[2];
}
this.setTime(da[1]);
}
}
this.updateValue();
} // eo function setValue


The modified code is following the "INFO" comment.

Your updated code worked perfectly. Thanks!

I also was having trouble setting the default time and date values, but I discovered that the UX is very picky with regards to formatting.

Dates have to be 04/01/2010 (leading zero) and Time has to be 06:00 (24 hour clock with leading zero).

Hope that saves someone else some frustration! :)

jsakalos
20 Aug 2010, 8:30 AM
@Lobo, have you tested it? I'd suppose that you should only add the line and not comment the existing lines.

Lobo
20 Aug 2010, 10:12 AM
@Lobo, have you tested it? I'd suppose that you should only add the line and not comment the existing lines.

So far it works based on the little testing I've done.

From what I've checked, the 'setValue' on DateTime already takes care of setting the passed (originalValue) date/time, so I believe it would be redundant if we don't comment those lines.

But then again, I have NOT tested all cases.

jsakalos
22 Aug 2010, 3:05 AM
Yeah, that's true. I haven't had sources handy when I answered, now I see that one call is enough. My very brief test also says that it works.

Jangla
23 Aug 2010, 2:56 AM
Can I ask - while the docs say the default hidden format is mysql format, when I ajax submit a form having collected the filed values with getFieldValue(), the date format submitted is actually JSON format? i.e. 2010-09-09T00:00:00 and not 2010-09-09 00:00:00 as expected.

jsakalos
23 Aug 2010, 3:06 AM
The plain DateField also returns Date object if you call getFieldValues() on the containing form. DateTime is consistent with this behavior. getFieldValues() loops through all underlying fields and calls their getValue() methods to build the return object.

notdet
23 Aug 2010, 8:27 AM
I see Datepicker not showing up when I migrated code from Ext 2.x to 3.x. Is there any change in the code for that ?

jsakalos
23 Aug 2010, 12:14 PM
Is your path in Ext.BLANK_IMAGE_URL still valid?

notdet
23 Aug 2010, 12:48 PM
Yes, blank_image_url is still a valid path.

jsakalos
23 Aug 2010, 10:05 PM
I cannot reproduce it - a form with DateTime run against ext-3.2.2 just shows correct. If your problem persists post a working showcase (http://www.sencha.com/learn/Ext_Forum_Help#Posting_a_working_showcase).

Jangla
26 Aug 2010, 2:46 AM
Got an issue with the control. If I load the details of a JsonStore into a form containing this control, like so:


myWin.findByType(Ext.form.FormPanel)[0].getForm().load({url : '/my/url/', params : { id : myId} }, waitMsg:'Loading...', success : doSomethingElse});...while the controls load in the data perfectly in FF, they're blank in Chrome.

Anyone else had this problem?

EDIT : Seems it could actually be the JsonStore. If I DON'T specify type='date' on the field in question, the JsonStore has a date in it in Chrome. If I DO specify the type, the JsonStore has a null in it for the date field. Any ideas why? Does Ext have an issue with date formats in Chrome?

notdet
26 Aug 2010, 3:17 AM
Thanks Saki, I was able to resolve this one. Its nothing to do with the plugin, but I had an override on Menu which caused the problem. Sorry for the wrong post :(

jsakalos
26 Aug 2010, 3:44 AM
@Jangla, type should be date and configured date format must match the format delivered from the server. Date is quite picky about that.

roger.harrington
3 Sep 2010, 8:04 AM
I needed to allow for having the datetime field to be readonly dynamically, so I patched in:

,setReadOnly : function(readOnly){
this.df.setReadOnly(readOnly);
this.tf.setReadOnly(readOnly);
this.readOnly = readOnly;
}

and in the onRender()
if (this.readOnly) {
this.setReadOnly(true);
}

uptodate
6 Oct 2010, 6:39 AM
hello,

when i change in a EditorGrid only the Time (with the ENTER-Key), the store do not update the record.

Code:


{
header : 'Datum - Zeit',
width : 120,
dataIndex : 'date',
xtype : 'datecolumn',
format : 'd.m.Y H:i:s',
filterable : true,
filter : {type: 'date', dateFormat: 'd.m.Y H:i:s'},
editor : new Ext.ux.form.DateTime({
dateFormat : 'd.m.Y',
timeFormat : 'H:i:s',
timePosition : 'below',
otherToNow : false,
timeConfig : {
format : 'H:i:s',
allowBlank: false
},
dateConfig : {
format : 'd.m.Y',
allowBlank : false
}
})
}

sean.zhou
6 Oct 2010, 4:16 PM
Hi Saki,

Inside of onSpecialKey() method, following code block


if(t === this.df && !e.shiftKey) {
e.stopEvent();
this.tf.focus();
}
if(t === this.tf && e.shiftKey) {
e.stopEvent();
this.df.focus();
}

seems not needed. The widget functions normally without it in ExtJs 3.2.1. Do you recall the reasons for having this code block initially?

Thanks,

Sean

jsakalos
6 Oct 2010, 11:43 PM
Tabbing when DateTime is in the grid.

uptodate
7 Oct 2010, 4:59 AM
hello,

when i change in a EditorGrid only the Time (with the ENTER-Key), the store do not update the record.

Code:


{
header : 'Datum - Zeit',
width : 120,
dataIndex : 'date',
xtype : 'datecolumn',
format : 'd.m.Y H:i:s',
filterable : true,
filter : {type: 'date', dateFormat: 'd.m.Y H:i:s'},
editor : new Ext.ux.form.DateTime({
dateFormat : 'd.m.Y',
timeFormat : 'H:i:s',
timePosition : 'below',
otherToNow : false,
timeConfig : {
format : 'H:i:s',
allowBlank: false
},
dateConfig : {
format : 'd.m.Y',
allowBlank : false
}
})
}

for this problem no solution ?

sean.zhou
7 Oct 2010, 7:55 AM
Tabbing when DateTime is in the grid.

This code block causes blur event from the datefield widget not fired after user tabbed out of the date widget into the time widget. Could you suggest a solution?

jsakalos
7 Oct 2010, 10:01 AM
Is it necessary for blur to fire when you tab between date and time? In fact, you're still inside the field. Blur should fire when you leave it. If it's really necessary for you, then you could manually fire it in the code.

sean.zhou
7 Oct 2010, 12:58 PM
Surprisingly, the blur event was fired in IE; just not in FireFox. Do you know why?

Jangla
20 Oct 2010, 12:53 AM
I can think of two excellent reasons to fire a blur when moving between date and time part of the field:
1. the user will see them as two fields
2. if you want to show a different tool tip for the date and time part of the control you need the blur to hide the first tip and show the second.

sean.zhou
25 Oct 2010, 10:07 PM
Hi Saki,

When the date field is empty, if the user filled in the time field and tab out, the time field will be reset to blank. However, if the user click elsewhere after filling in the time field, the date will be set to today and the time will remain what user entered.

I was trying to figure out why there is such a difference and make the tab out work as what click elsewhere does. It seems that multiple events were triggered and I was not able to pin point the cause. Would you shed some light to the problem?

Thanks.

annextjs
26 Oct 2010, 3:12 AM
Hi Saki,

I am trying to use DateTime field since two days as a editor in EditorGridPanel. Have been in trouble with few things.
My date for is m/d and time format is H:i A. The date coming from database is in Y-m-d H:i:s format.

1. Though I have given editable:false in dateConfig, it still allows me to enter the value in date field.
2. The date is being populated in date field but it always says "This date should be equal or after some date" But the date populated and date shown in error message are same.

When i change the code to this.df.setEditable(false), it said there is not such property.

Here is my DateTime editor


editor: new Ext.ux.form.DateTime({
id:'dtf',
fieldLabel:'Date & Time',
anchor:'-18',
timePosition: 'below',
timeFormat:'H:i A',
timeConfig: {
altFormats:'H:i:s',
allowBlank:false
},
dateFormat:'m/d',
dateConfig: {
editable: false,
minValue : tDate,
maxValue : ttDate,
altFormats:'Y-m-d|Y-n-d',
allowBlank:false,
validationEvent: false,
validateOnBlur: false
},
listeners: {
focus: function(){
this.df.setValue(this.getValue());
this.df.updateValue();
this.df.setEditable(false);
}
}
})

mschwartz
2 Nov 2010, 12:52 PM
Saki

I am using this extension and it's quite nice. However, if you use it as a field in a tab not shown or a card in a cardlayout not shown, when you switch to that tab or card, it is rendered all messed up. The time field is on top of the date field/picker. Doesn't matter if there's deferredRender true or false, and it doesn't matter if I doLayout() when the tab/card is activated.

I have used it in two applications that both exhibit the same behavior, one that uses zero additional CSS, so that's not the issue.

If the field is on the first card or tab, it renders fine.