Animal
4 May 2007, 5:22 AM
Certain Field types, which are validated using a minimum and maximum value will very often have to be validated as a pair using the "other" Field's value, rather than anything decided by the JSP author at authoring time, or at page generation time.
The two cases currently are NumberField and DateField
To support this, a Field needs to be able to find a sibling Field by name. That should be a fairly core piece of functionality. It's supported by straight HTML input elements. Each element has a reference to its form element.
To support this, Ext.form.BasicForm.add(Field...) has to set the form in each of the Fields that it is adding.
Once this is supported, cross-validation can be done.
Please can we add this? I think it is a very basic and commonly used pattern.
Here's my override code, and it works very nicely with two related NumberFields:
Ext.override(Ext.form.BasicForm, {
/**
* Add Ext.form components to this form
* @param {Field} field1
* @param {Field} field2 (optional)
* @param {Field} etc (optional)
*/
add : function(){
for (var i = 0; i < arguments.length; i++) {
arguments[i].setForm(this);
this.items.add(arguments[i]);
}
}
});
Ext.override(Ext.form.Field, {
// private
setForm: function(f){
this.form = f;
},
// private
getForm: function(f){
return this.form;
},
/**
* Get a sibling field by name.
* @param {String} n The name of the sibling field to find.
* @return {Object} The Ext.form.Field instance in the form with the specified name.
*/
getSibling: function(n) {
if (n instanceof Ext.form.Field) {
return n;
}
var f = this.getForm();
if (f) {
return f.findField(n);
}
},
// private
onBlur : function(){
this.el.removeClass(this.focusClass);
this.hasFocus = false;
if (this.forceUpperCase) { // The only addition here!
this.el.dom.value = String(this.getValue()).toUpperCase();
}
if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
this.validate();
}
var v = this.getValue();
if(v != this.startValue){
this.fireEvent('change', this, v, this.startValue);
}
this.fireEvent("blur", this);
},
applyTo : function(target){
this.target = target;
this.el = Ext.get(target);
this.render(this.el.dom.parentNode);
Ext.get(this.el.dom.parentNode).addClass("x-form-element"); // Was missing
return this;
}
});
Ext.override(Ext.form.NumberField, {
// private
validateValue : function(value){
if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
return false;
}
if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
return true;
}
value = String(value).replace(this.decimalSeparator, ".");
if(isNaN(value)){
this.markInvalid(String.format(this.nanText, value));
return false;
}
var num = this.parseValue(value);
var min = this.getValidationValue(this.minValue);
var max = this.getValidationValue(this.maxValue);
if(num < min.value){
this.markInvalid(String.format(this.minText, min.value));
this.crossValidate(min, max);
return false;
}
if(num > max.value){
this.markInvalid(String.format(this.maxText, max.value));
this.crossValidate(min, max);
return false;
}
this.crossValidate(min, max);
return true;
},
crossValidate: function(min, max) {
// If two fields depend upon each other's values, cross-validate.
if (!this.isValidating) {
this.isValidating = true;
if (min.otherField) {
min.otherField.validate();
}
if (max.otherField) {
max.otherField.validate();
}
this.isValidating = false;
}
},
getValidationValue: function(v) {
// If it's a string, use it as a reference to a sibling
if(isNaN(v)) {
var o = this.getSibling(v);
if (o) return {otherField: o, value: this.parseValue(o.getValue())};
} else {
return {otherField: false, value: v};
}
return {value: Number.NaN};
}
});
The two cases currently are NumberField and DateField
To support this, a Field needs to be able to find a sibling Field by name. That should be a fairly core piece of functionality. It's supported by straight HTML input elements. Each element has a reference to its form element.
To support this, Ext.form.BasicForm.add(Field...) has to set the form in each of the Fields that it is adding.
Once this is supported, cross-validation can be done.
Please can we add this? I think it is a very basic and commonly used pattern.
Here's my override code, and it works very nicely with two related NumberFields:
Ext.override(Ext.form.BasicForm, {
/**
* Add Ext.form components to this form
* @param {Field} field1
* @param {Field} field2 (optional)
* @param {Field} etc (optional)
*/
add : function(){
for (var i = 0; i < arguments.length; i++) {
arguments[i].setForm(this);
this.items.add(arguments[i]);
}
}
});
Ext.override(Ext.form.Field, {
// private
setForm: function(f){
this.form = f;
},
// private
getForm: function(f){
return this.form;
},
/**
* Get a sibling field by name.
* @param {String} n The name of the sibling field to find.
* @return {Object} The Ext.form.Field instance in the form with the specified name.
*/
getSibling: function(n) {
if (n instanceof Ext.form.Field) {
return n;
}
var f = this.getForm();
if (f) {
return f.findField(n);
}
},
// private
onBlur : function(){
this.el.removeClass(this.focusClass);
this.hasFocus = false;
if (this.forceUpperCase) { // The only addition here!
this.el.dom.value = String(this.getValue()).toUpperCase();
}
if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
this.validate();
}
var v = this.getValue();
if(v != this.startValue){
this.fireEvent('change', this, v, this.startValue);
}
this.fireEvent("blur", this);
},
applyTo : function(target){
this.target = target;
this.el = Ext.get(target);
this.render(this.el.dom.parentNode);
Ext.get(this.el.dom.parentNode).addClass("x-form-element"); // Was missing
return this;
}
});
Ext.override(Ext.form.NumberField, {
// private
validateValue : function(value){
if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
return false;
}
if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
return true;
}
value = String(value).replace(this.decimalSeparator, ".");
if(isNaN(value)){
this.markInvalid(String.format(this.nanText, value));
return false;
}
var num = this.parseValue(value);
var min = this.getValidationValue(this.minValue);
var max = this.getValidationValue(this.maxValue);
if(num < min.value){
this.markInvalid(String.format(this.minText, min.value));
this.crossValidate(min, max);
return false;
}
if(num > max.value){
this.markInvalid(String.format(this.maxText, max.value));
this.crossValidate(min, max);
return false;
}
this.crossValidate(min, max);
return true;
},
crossValidate: function(min, max) {
// If two fields depend upon each other's values, cross-validate.
if (!this.isValidating) {
this.isValidating = true;
if (min.otherField) {
min.otherField.validate();
}
if (max.otherField) {
max.otherField.validate();
}
this.isValidating = false;
}
},
getValidationValue: function(v) {
// If it's a string, use it as a reference to a sibling
if(isNaN(v)) {
var o = this.getSibling(v);
if (o) return {otherField: o, value: this.parseValue(o.getValue())};
} else {
return {otherField: false, value: v};
}
return {value: Number.NaN};
}
});