/**
* @class Ext.util.Stateful
* Represents any object whose data can be saved by a {@link Ext.data.Proxy Proxy}. Ext.Model
* and Ext.View both inherit from this class as both can save state (Models save field state,
* Views save configuration)
*/
Ext.define('Ext.util.Stateful', {
mixins: {
observable: 'Ext.util.Observable'
},
/**
* Internal flag used to track whether or not the model instance is currently being edited. Read-only
* @property editing
* @type Boolean
*/
editing : false,
/**
* Readonly flag - true if this Record has been modified.
* @type Boolean
*/
dirty : false,
/**
* @cfg {String} persistanceProperty The property on this Persistable object that its data is saved to.
* Defaults to 'data' (e.g. all persistable data resides in this.data.)
*/
persistanceProperty: 'data',
constructor: function(config) {
Ext.applyIf(this, {
data: {}
});
/**
* Key: value pairs of all fields whose values have changed
* @property modified
* @type Object
*/
this.modified = {};
this[this.persistanceProperty] = {};
this.mixins.observable.constructor.call(this);
},
/**
* Returns the value of the given field
* @param {String} fieldName The field to fetch the value for
* @return {Mixed} The value
*/
get: function(field) {
return this[this.persistanceProperty][field];
},
/**
* Sets the given field to the given value, marks the instance as dirty
* @param {String|Object} fieldName The field to set, or an object containing key/value pairs
* @param {Mixed} value The value to set
*/
set: function(fieldName, value) {
var fields = this.fields,
convertFields = [],
field, key, i;
/*
* If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
* set those last so that all other possible data is set before the convert function is called
*/
if (arguments.length == 1 && Ext.isObject(fieldName)) {
for (key in fieldName) {
if (!fieldName.hasOwnProperty(key)) {
continue;
}
//here we check for the custom convert function. Note that if a field doesn't have a convert function,
//we default it to its type's convert function, so we have to check that here. This feels rather dirty.
field = fields.get(key);
if (field && field.convert !== field.type.convert) {
convertFields.push(key);
continue;
}
this.set(key, fieldName[key]);
}
for (i = 0; i < convertFields.length; i++) {
field = convertFields[i];
this.set(field, fieldName[field]);
}
} else {
if (fields) {
field = fields.get(fieldName);
if (field && field.convert) {
value = field.convert(value, this);
}
}
this[this.persistanceProperty][fieldName] = value;
this.dirty = true;
if (!this.editing) {
this.afterEdit();
}
}
},
/**
* Gets a hash of only the fields that have been modified since this Model was created or commited.
* @return Object
*/
getChanges : function(){
var modified = this.modified,
changes = {},
field;
for (field in modified) {
if (modified.hasOwnProperty(field)){
changes[field] = this[this.persistanceProperty][field];
}
}
return changes;
},
/**
* Returns true if the passed field name has been {@link #modified}
* since the load or last commit.
* @param {String} fieldName {@link Ext.data.Field#name}
* @return {Boolean}
*/
isModified : function(fieldName) {
return !!(this.modified && this.modified.hasOwnProperty(fieldName));
},
/**
* Marks this Record as {@link #dirty}. This method
* is used interally when adding {@link #phantom} records to a
* {@link Ext.data.Store#writer writer enabled store}.
*
Marking a record {@link #dirty} causes the phantom to
* be returned by {@link Ext.data.Store#getModifiedRecords} where it will
* have a create action composed for it during {@link Ext.data.Store#save store save}
* operations.
*/
setDirty : function() {
this.dirty = true;
if (!this.modified) {
this.modified = {};
}
this.fields.each(function(field) {
this.modified[field.name] = this[this.persistanceProperty][field.name];
}, this);
},
//
markDirty : function() {
throw new Error("Stateful: markDirty has been deprecated. Please use setDirty.");
},
//
/**
* Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}.
* Rejects all changes made to the model instance since either creation, or the last commit operation.
* Modified fields are reverted to their original values.
* Developers should subscribe to the {@link Ext.data.Store#update} event
* to have their code notified of reject operations.
* @param {Boolean} silent (optional) True to skip notification of the owning
* store of the change (defaults to false)
*/
reject : function(silent) {
var modified = this.modified,
field;
for (field in modified) {
if (!modified.hasOwnProperty(field)) {
continue;
}
if (typeof modified[field] != "function") {
this[this.persistanceProperty][field] = modified[field];
}
}
this.dirty = false;
this.editing = false;
delete this.modified;
if (silent !== true) {
this.afterReject();
}
},
/**
* Usually called by the {@link Ext.data.Store} which owns the model instance.
* Commits all changes made to the instance since either creation or the last commit operation.
* Developers should subscribe to the {@link Ext.data.Store#update} event
* to have their code notified of commit operations.
* @param {Boolean} silent (optional) True to skip notification of the owning
* store of the change (defaults to false)
*/
commit : function(silent) {
this.dirty = false;
this.editing = false;
delete this.modified;
if (silent !== true) {
this.afterCommit();
}
},
/**
* Creates a copy (clone) of this Model instance.
* @param {String} id (optional) A new id, defaults to the id
* of the instance being copied. See {@link #id}.
* To generate a phantom instance with a new id use:
var rec = record.copy(); // clone the record
Ext.data.Model.id(rec); // automatically generate a unique sequential id
*
* @return {Record}
*/
copy : function(newId) {
return new this.self(Ext.apply({}, this[this.persistanceProperty]), newId || this.internalId);
}
});