date

Ext JS 4: Date Display Field

Ext JS Ext.form.field.Display works well for string text, but doesn’t render dates easily. Turns out you can use the method valueToRaw to apply a formatter. Here’s a simple extension for date fields:

This is 100% inspired by this post http://www.sencha.com/forum/showthread.php?148013-How-to-invoke-a-renderer-on-a-displayfield-xtype on the Sencha forums.

To use it’s as simple as having a config on your form like so:

{
  xtype: 'datedisplayfield',
  fieldLabel : 'My Date',
  name : 'myDate'
  datePattern: 'l, F d, Y g:i:s A'
}

datePattern is optional, default it ‘Y-m-d’.

Version Info: Ext JS 4.1.1

Ext JS 4 – DateRange VType

I needed date range validation for two date fields in my application. I started with the VType in the Advanced Validation example on Sencha’s site.

I have to admit that I was having one of those stupid/rushed days where you are trying to move a little too fast and therefore you miss some basics. What I learned about this validation:

  1. To your start date field, add a property “endDateField” which has the id/itemid of the end date field. This is used during the navigation to know if the date needs to be validated against the opposite date. On the end date field there is a similar property, but this one is named startDateField.
  2. Add a property to both date fields named “vfield”. This property is used by the component queries to get the opposite date. For the start date field it have a value of something like “startDate” or “beginDate”.
  3. Apply the vtype to the date field, in the standard way
  4. Do not put an initial value on these date fields. If you do, you will end up in an infinite loop. It is not pretty.

I implemented three changes to the vtype validation function used in the examples.

  1. The first one is from the forums: http://www.sencha.com/forum/showthread.php?153097-daterange-is-giving-error-for-start-date-and-end-date. It recommends adding the vfield property to make the validation a little more generic.
  2. The code in the examples goes up from the field looking for a ‘form’. In my case, I am going to be using this validation in two places on the same card. Once in a toolbar (no form) and once in a form. In order to reuse this code I changed it to query up for the date field’s ownerCt xtype.
  3. Last but not least. The example code is setting a property on the vtype itself. The dateRangeMin and dateRangeMax. Since I am using this validation in more than one place in the same app, I changed this and set those properties on the field instead of on the vtype.
  4. Here is the vtype code. Not sure why, but I changed the names around a little bit (begin vs. start).

    
    Ext.apply(Ext.form.field.VTypes, {
    	DateRange: function(val, field) {
    		var date = field.parseDate(val);
    
    		if (!date) {
    			return false;
    		}
    		if (field.startDateField && (!field.dateRangeMax || (date.getTime() != field.dateRangeMax.getTime()))) {
    			var start = field.up(field.ownerCt.xtype).down('datefield[vfield=beginDate]');
    			start.setMaxValue(date);
    			start.validate();
    			field.dateRangeMax = date;
    		}
    		else if (field.endDateField && (!field.dateRangeMin || (date.getTime() != field.dateRangeMin.getTime()))) {
    			var end = field.up(field.ownerCt.xtype).down('datefield[vfield=endDate]');
    			end.setMinValue(date);
    			end.validate();
    			field.dateRangeMin = date;
    		}
    		/*
    		 * Always return true since we're only using this vtype to set the
    		 * min/max allowed values (these are tested for after the vtype test)
    		 */
    		return true;
    	},
    	DateRangeText: 'From date must be before To date'
    });
    

    And here are the configs of the date fields (these date fields are in a toolbar, hence no fieldLabel):

    'From:',
    {
    	xtype: 'datefield',
    	itemId: 'masterBegin',
    	width: 100,
    	allowBlank: false,
    	vfield: 'beginDate',
    	endDateField: 'masterEnd',
    	vtype: 'DateRange'
    },
    'To:',
    {
    	xtype: 'datefield',
    	itemId: 'masterEnd',
    	width: 100,
    	allowBlank: false,
    	vfield: 'endDate',
    	startDateField: 'masterStart',
    	vtype: 'DateRange'
    }
    

    One last thing. I wanted these date fields to have default values, but if I set them in the configs nothing would work. Instead, I settled on setting the values after render.

    afterRender : function () {
    	this.callParent(arguments);
    
    	var endDt = new Date(),
    		beginDt = Ext.Date.add(endDt, Ext.Date.DAY, -7);
    
    	this.down('#masterBegin').setValue(beginDt);
    	this.down('#masterEnd').setValue(endDt);
    
    	//...
    }
    

    Update 02/07/2012

    Everything from above was working well, except for one thing. I am using these date fields in a search criteria panel and I have a “Reset” link to easily clear out all the search fields. It looks like this:

    The date gets cleared out, but the picker dates are still disabled. I can figure out how to navigate the dates to get what I want but I can’t expect a user to understand that the dates were cleared but the picker was not. So, I ended up updating my DateRange VType to handle null dates. I removed the check for null before the validation and allowed null dates through the validations. My DateRange validation now looks like this:

    DateRange: function(val, field) {
    	var date = field.parseDate(val);
    
    	if (field.startDateField &&
    		(!field.dateRangeMax || date === null || (date.getTime() != field.dateRangeMax.getTime()) )) {
    		var start = field.up(field.ownerCt.xtype).down('datefield[vfield=beginDate]');
    		start.setMaxValue(date);
    		start.validate();
    		field.dateRangeMax = date;
    	}
    	else if (field.endDateField &&
    		(!field.dateRangeMin  || date === null || (date.getTime() != field.dateRangeMin.getTime()) )) {
    		var end = field.up(field.ownerCt.xtype).down('datefield[vfield=endDate]');
    		end.setMinValue(date);
    		end.validate();
    		field.dateRangeMin = date;
    	}
    	/*
    	 * Always return true since we're only using this vtype to set the
    	 * min/max allowed values (these are tested for after the vtype test)
    	 */
    	return true;
    },
    DateRangeText: 'From date must be before To date'
    

    Next, I had to get the validation called when the date was cleared. The date fields allow blanks, so during normal validation the vtypes are never called if the value is empty. So, I had to force the call by adding a listener and explicitly calling the DateRange Vtype to the date field configs.

    {
    	itemId: 'detailBegin',
    	name: 'beginDt',
    	xtype: 'datefield',
    	fieldLabel: 'Begin Date',
    	margins: '0 0 0 5',
    	vfield: 'beginDate',
    	endDateField: 'detailEnd',
    	vtype: 'DateRange',
    	plugins: ['clearbutton'],
    	listeners : {
    		scope: this,
    		change: function(field, newValue, oldValue) {
    			if (newValue === null) {
    				Ext.form.field.VTypes.DateRange(newValue, field);
    			}
    		}
    	}
    },
    {
    	itemId: 'detailEnd',
    	name: 'endDt',
    	xtype: 'datefield',
    	fieldLabel: 'End Date',
    	margins: '0 0 0 5',
    	vfield: 'endDate',
    	startDateField: 'detailBegin',
    	vtype: 'DateRange',
    	plugins: ['clearbutton'],
    	listeners : {
    		scope: this,
    		change: function(field, newValue, oldValue) {
    			if (newValue === null) {
    				Ext.form.field.VTypes.DateRange(newValue, field);
    			}
    		}
    	}
    }
    

Ext JS – Serialize Date using Jackson and Spring 3

From bottom to top:

  1. In the database (MS SQLServer): column is a datetime (aka, a timestamp)
  2. In domain object, need the @Temporal(TemporalType.TIMESTAMP) annotation (using Hibernate 3x). Note, I also added @JsonProperty for Jackson b/c I wanted a shorter name in the JSON.
    	@Temporal(TemporalType.TIMESTAMP)
    	@Column(name="REGISTRATION_DATE")
    	private Date registrationDate;
    	@JsonProperty("regDate")
    	public Date getRegistrationDate() { return registrationDate; }
    	public void setRegistrationDate(Date registrationDate) { this.registrationDate = registrationDate; }
    
  3. Configured Jackson object mapper to use ISO-8601 format
    objectMapper.configure(SerializationConfig.WRITE_DATES_AS_TIMESTAMPS, false);
    
  4. Define field in Ext JS 4 model as a Date type, using the ‘c’ date format.
    Ext.define('User', {
    	extend: 'Ext.data.Model',
    	fields: [
    		{name: 'regDate', type: 'date', dateFormat: 'c'}
    	]
    });
    
  5. Show the date in a user friendly format in the grid. Ext JS 4, column config:
    {text: 'Registration Date', dataIndex: 'regDate', sortable: true, xtype: 'datecolumn', format:'m-d-Y'}
    

Helpful Links