/**
* This class is used to hold validation errors for a record. The results of the record's
* `{@link Ext.data.Model#validators validators}` are stored as the field values of this
* record. The first failed validation is all that is stored per field unless the Model
* class has defined a `validationSeparator` config.
* @since 5.0.0
*/
Ext.define('Ext.data.Validation', {
extend: 'Ext.data.Model',
isValidation: true,
/**
* @property {Number} syncGeneration
* This is a capture of the `{@link Ext.data.Model#generation}` value from the last
* time the validation state was refreshed. This is used to determine if this object
* is potentially out-of-date with its associated `record`.
* @private
* @readonly
* @since 5.0.0
*/
syncGeneration: 0, // Model generation starts at 1 so we start out-of-sync
/**
* Attaches this instance to its associated `record`.
* @param {Ext.data.Model} record The associated record.
* @private
* @since 5.0.0
*/
attach: function(record) {
/**
* @property {Ext.data.Model} record
* The associated record for this validation instance.
* @readonly
* @since 5.0.0
*/
this.record = record;
this.isBase = record.self === Ext.data.Model;
// We need to remove the id property from our data as that is not meaningful for
// a Validation pseudo-record.
delete this.data.id;
},
getValidation: function() {
return null;
},
/**
* Returns true if the associated record (not this one) is valid.
* @return {Boolean}
*/
isValid: function() {
var me = this;
if (me.syncGeneration !== me.record.generation) {
me.refresh();
}
return !me.dirty;
},
/**
* This method updates the state of this record against its associated `record`. This
* method should not need to be called directly as it is internally called when this
* record is returned by `{@link Ext.data.Model#getValidation}`.
* @param {Boolean} [force=false] Pass `true` to force an update of validation state.
* @private
* @since 5.0.0
*/
refresh: function(force) {
// If it's an Ext.data.Model instance directly, we can't
// validate it because there can be no fields/validators.
if (this.isBase) {
return;
}
/* eslint-disable-next-line vars-on-top */
var me = this,
data = me.data,
record = me.record,
fields = record.fields,
generation = record.generation,
recordData = record.data,
sep = record.validationSeparator,
values = null,
defaultMessage, currentValue, error, field,
i, len, msg, val, name;
if (force || me.syncGeneration !== generation) {
me.syncGeneration = generation;
for (i = 0, len = fields.length; i < len; ++i) {
field = fields[i];
name = field.name;
val = recordData[name];
defaultMessage = field.defaultInvalidMessage;
error = 0;
if (!(name in data)) {
// Now is the cheapest time to populate our data object with "true"
// for all validated fields. This ensures that our non-dirty state
// equates to isValid.
data[name] = currentValue = true; // true === valid
}
else {
currentValue = data[name];
}
if (field.validate !== Ext.emptyFn) {
msg = field.validate(val, sep, null, record);
if (msg !== true) {
error = msg || defaultMessage;
}
}
if (!error) {
error = true; // valid state is stored as true
}
if (error !== currentValue) {
(values || (values = {}))[name] = error;
}
}
if (values) {
// only need to do this if something changed...
me.set(values);
}
}
}
});