PDA

View Full Version : Amazing vanishing markInvalid validation and the date picker datefield component



ogradyjd
1 Aug 2012, 4:14 AM
Version: ExtJS 4.1

My question is this: How do I use markInvalid on a datefield when the built-in validation continually gets called in the background for many different events, removing the markInvalid I put on it.

The scenario is well known - I have two datefields - a start and an end date picker. If the user selects a combination of dates where the start date is after the end date, I call "markInvalid" on the end date datefield. The problem is that I can't make the markInvalid stick. The workflow looks like this...
The user selects a date from a picker that puts the start date after the end date
Change event fires, calls my validate method where I mark end date as invalid.
background validator runs and removes my invalid mark.
Select event fires, calls my validate method where I mark end date as invalid.
User clicks away from datefield component.
background validator runs and removes my invalid mark.
Blur even fires, calls my validate method where I mark end date as invalid.
I checked the events, inherited and otherwise, for the datefield component and found none that would allow me to run my validator just once after the background validator ran. Also, there does not seem to be a way to override the background validator method from the configuration, where I would call the parent validator, then run my validation code last. It seems really bad to have to run my validate code two times per select just so I can also cover the user manually inputting a date (where only the change event gets called). And even then, the user could still manually input a bad date (according to my validator) and no event gets called after the background validator clears the markInvalid that I can use to put the markInvalid back.

Can anyone point me to the right/more elegant way of handling this?



validateDates: function() {
if(!this._startDatePickCtrl || !this._endDatePickCtrl) {
return;
}
if(!this._startDatePickCtrl.isValid() || !this._endDatePickCtrl.isValid()) {
this._toggleSubmit(false);
return;
}
if(this.getSelectedStartDate()>this.getSelectedEndDate()) {
this._toggleSubmit(false);
this._endDatePickCtrl.markInvalid('The end date cannot be before the start date.');
return;
}
this._endDatePickCtrl.clearInvalid();
this._startDatePickCtrl.clearInvalid();
this._toggleSubmit(true);
},


this._endDatePickCtrl = new Ext.form.field.Date({
renderTo: 'endDatePicker',
name: 'endDatePickerVal',
itemId: 'endDatePicker',
showToday: false,
disabled: true,
checkChangeBuffer: 300,
listeners: {
'change': function(field, value, options) {
context.validateDates();
},
'validityChange': function(field,isValid,options) {
context.validateDates();
},
'select': function() {
context.validateDates();
},
'blur': function(field, event, eOpts) {
context.validateDates();
}
}
});

mike young
13 Aug 2012, 5:46 AM
An option is to define a custom vtype. This will be a much cleaner approach rather than intercepting all those events. See below.

An example of the custom 'daterange' vtype:



Ext.onReady(function() {


Ext.create('Ext.panel.Panel', {
layout: 'anchor',
border: false,
renderTo : Ext.getBody(),
items : [{
xtype: 'form',
border: false,
buttonAlign: 'left',
items:[{
xtype: 'datefield',
name: 'from',
fieldLabel: 'From',
format : 'd/m/Y',
vtype : 'daterange',
endDateField : 'to',
allowBlank : false
}, {
xtype: 'datefield',
name: 'to',
fieldLabel: 'To',
format : 'd/m/Y',
vtype : 'daterange',
startDateField : 'from',
allowBlank : false
}],


buttons: [{
text: 'Submit',
formBind: true,
handler: function(btn, eopts) {
if(btn.up('form').getForm().isValid()){
alert('i am valid :)');
}
}
}],

listeners : {
afterrender : function(form, eopts) {
Ext.apply(Ext.form.field.VTypes, {
daterange : function(val, field) {
var date = field.parseDate(val);

if (!date) {
return false;
}
if (field.startDateField && (!this.dateRangeMax || (date.getTime() != this.dateRangeMax.getTime()))) {
var start = form.down('datefield[name='+field.startDateField+']');
start.setMaxValue(date);
this.dateRangeMax = date;
} else if (field.endDateField && (!this.dateRangeMin || (date.getTime() != this.dateRangeMin.getTime()))) {
var end = form.down('datefield[name='+field.endDateField+']');
end.setMinValue(date);
this.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 : 'Start date must be less than end date'
});
}
}
}]
});
});