X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/7a654f8d43fdb43d78b63d90528bed6e86b608cc..refs/heads/master:/docs/source/Model.html diff --git a/docs/source/Model.html b/docs/source/Model.html index 596a172b..f199d143 100644 --- a/docs/source/Model.html +++ b/docs/source/Model.html @@ -1,235 +1,233 @@ -
/**
+
+
+
+
+ The source code
+
+
+
+
+
+
+ /**
* @author Ed Spencer
- * @class Ext.data.Model
*
- * <p>A Model represents some object that your application manages. For example, one might define a Model for Users, Products,
- * Cars, or any other real-world object that we want to model in the system. Models are registered via the {@link Ext.ModelManager model manager},
- * and are used by {@link Ext.data.Store stores}, which are in turn used by many of the data-bound components in Ext.</p>
+ * A Model represents some object that your application manages. For example, one might define a Model for Users,
+ * Products, Cars, or any other real-world object that we want to model in the system. Models are registered via the
+ * {@link Ext.ModelManager model manager}, and are used by {@link Ext.data.Store stores}, which are in turn used by many
+ * of the data-bound components in Ext.
*
- * <p>Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:</p>
+ * Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:
*
-<pre><code>
-Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: [
- {name: 'name', type: 'string'},
- {name: 'age', type: 'int'},
- {name: 'phone', type: 'string'},
- {name: 'alive', type: 'boolean', defaultValue: true}
- ],
-
- changeName: function() {
- var oldName = this.get('name'),
- newName = oldName + " The Barbarian";
-
- this.set('name', newName);
- }
-});
-</code></pre>
-*
-* <p>The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link Ext.ModelManager ModelManager}, and all
-* other functions and properties are copied to the new Model's prototype.</p>
-*
-* <p>Now we can create instances of our User model and call any model logic we defined:</p>
-*
-<pre><code>
-var user = Ext.ModelManager.create({
- name : 'Conan',
- age : 24,
- phone: '555-555-5555'
-}, 'User');
-
-user.changeName();
-user.get('name'); //returns "Conan The Barbarian"
-</code></pre>
+ * Ext.define('User', {
+ * extend: 'Ext.data.Model',
+ * fields: [
+ * {name: 'name', type: 'string'},
+ * {name: 'age', type: 'int'},
+ * {name: 'phone', type: 'string'},
+ * {name: 'alive', type: 'boolean', defaultValue: true}
+ * ],
*
- * <p><u>Validations</u></p>
+ * changeName: function() {
+ * var oldName = this.get('name'),
+ * newName = oldName + " The Barbarian";
*
- * <p>Models have built-in support for validations, which are executed against the validator functions in
- * {@link Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to models:</p>
+ * this.set('name', newName);
+ * }
+ * });
*
-<pre><code>
-Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: [
- {name: 'name', type: 'string'},
- {name: 'age', type: 'int'},
- {name: 'phone', type: 'string'},
- {name: 'gender', type: 'string'},
- {name: 'username', type: 'string'},
- {name: 'alive', type: 'boolean', defaultValue: true}
- ],
-
- validations: [
- {type: 'presence', field: 'age'},
- {type: 'length', field: 'name', min: 2},
- {type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
- {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
- {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
- ]
-});
-</code></pre>
+ * The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link
+ * Ext.ModelManager ModelManager}, and all other functions and properties are copied to the new Model's prototype.
*
- * <p>The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
- * object:</p>
+ * Now we can create instances of our User model and call any model logic we defined:
*
-<pre><code>
-var instance = Ext.ModelManager.create({
- name: 'Ed',
- gender: 'Male',
- username: 'edspencer'
-}, 'User');
-
-var errors = instance.validate();
-</code></pre>
+ * var user = Ext.create('User', {
+ * name : 'Conan',
+ * age : 24,
+ * phone: '555-555-5555'
+ * });
*
- * <p><u>Associations</u></p>
+ * user.changeName();
+ * user.get('name'); //returns "Conan The Barbarian"
*
- * <p>Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and
- * {@link Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
- * application which deals with Users, Posts and Comments. We can express the relationships between these models like this:</p>
+ * # Validations
*
-<pre><code>
-Ext.define('Post', {
- extend: 'Ext.data.Model',
- fields: ['id', 'user_id'],
-
- belongsTo: 'User',
- hasMany : {model: 'Comment', name: 'comments'}
-});
-
-Ext.define('Comment', {
- extend: 'Ext.data.Model',
- fields: ['id', 'user_id', 'post_id'],
-
- belongsTo: 'Post'
-});
-
-Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: ['id'],
-
- hasMany: [
- 'Post',
- {model: 'Comment', name: 'comments'}
- ]
-});
-</code></pre>
+ * Models have built-in support for validations, which are executed against the validator functions in {@link
+ * Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to
+ * models:
*
- * <p>See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the usage
- * and configuration of associations. Note that associations can also be specified like this:</p>
+ * Ext.define('User', {
+ * extend: 'Ext.data.Model',
+ * fields: [
+ * {name: 'name', type: 'string'},
+ * {name: 'age', type: 'int'},
+ * {name: 'phone', type: 'string'},
+ * {name: 'gender', type: 'string'},
+ * {name: 'username', type: 'string'},
+ * {name: 'alive', type: 'boolean', defaultValue: true}
+ * ],
*
-<pre><code>
-Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: ['id'],
-
- associations: [
- {type: 'hasMany', model: 'Post', name: 'posts'},
- {type: 'hasMany', model: 'Comment', name: 'comments'}
- ]
-});
-</code></pre>
+ * validations: [
+ * {type: 'presence', field: 'age'},
+ * {type: 'length', field: 'name', min: 2},
+ * {type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
+ * {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
+ * {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
+ * ]
+ * });
*
- * <p><u>Using a Proxy</u></p>
+ * The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
+ * object:
*
- * <p>Models are great for representing types of data and relationships, but sooner or later we're going to want to
- * load or save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy},
- * which can be set directly on the Model:</p>
+ * var instance = Ext.create('User', {
+ * name: 'Ed',
+ * gender: 'Male',
+ * username: 'edspencer'
+ * });
*
-<pre><code>
-Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: ['id', 'name', 'email'],
-
- proxy: {
- type: 'rest',
- url : '/users'
- }
-});
-</code></pre>
+ * var errors = instance.validate();
*
- * <p>Here we've set up a {@link Ext.data.proxy.Rest Rest Proxy}, which knows how to load and save data to and from a
- * RESTful backend. Let's see how this works:</p>
+ * # Associations
*
-<pre><code>
-var user = Ext.ModelManager.create({name: 'Ed Spencer', email: 'ed@sencha.com'}, 'User');
-
-user.save(); //POST /users
-</code></pre>
+ * Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and {@link
+ * Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
+ * application which deals with Users, Posts and Comments. We can express the relationships between these models like
+ * this:
*
- * <p>Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this
- * Model's data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't
- * have an id, and performs the appropriate action - in this case issuing a POST request to the url we configured
- * (/users). We configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full
- * list.</p>
+ * Ext.define('Post', {
+ * extend: 'Ext.data.Model',
+ * fields: ['id', 'user_id'],
*
- * <p>Loading data via the Proxy is equally easy:</p>
+ * belongsTo: 'User',
+ * hasMany : {model: 'Comment', name: 'comments'}
+ * });
*
-<pre><code>
-//get a reference to the User model class
-var User = Ext.ModelManager.getModel('User');
-
-//Uses the configured RestProxy to make a GET request to /users/123
-User.load(123, {
- success: function(user) {
- console.log(user.getId()); //logs 123
- }
-});
-</code></pre>
+ * Ext.define('Comment', {
+ * extend: 'Ext.data.Model',
+ * fields: ['id', 'user_id', 'post_id'],
*
- * <p>Models can also be updated and destroyed easily:</p>
+ * belongsTo: 'Post'
+ * });
*
-<pre><code>
-//the user Model we loaded in the last snippet:
-user.set('name', 'Edward Spencer');
-
-//tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
-user.save({
- success: function() {
- console.log('The User was updated');
- }
-});
-
-//tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
-user.destroy({
- success: function() {
- console.log('The User was destroyed!');
- }
-});
-</code></pre>
+ * Ext.define('User', {
+ * extend: 'Ext.data.Model',
+ * fields: ['id'],
*
- * <p><u>Usage in Stores</u></p>
+ * hasMany: [
+ * 'Post',
+ * {model: 'Comment', name: 'comments'}
+ * ]
+ * });
*
- * <p>It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this
- * by creating a {@link Ext.data.Store Store}:</p>
+ * See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the
+ * usage and configuration of associations. Note that associations can also be specified like this:
*
-<pre><code>
-var store = new Ext.data.Store({
- model: 'User'
-});
-
-//uses the Proxy we set up on Model to load the Store data
-store.load();
-</code></pre>
+ * Ext.define('User', {
+ * extend: 'Ext.data.Model',
+ * fields: ['id'],
+ *
+ * associations: [
+ * {type: 'hasMany', model: 'Post', name: 'posts'},
+ * {type: 'hasMany', model: 'Comment', name: 'comments'}
+ * ]
+ * });
+ *
+ * # Using a Proxy
+ *
+ * Models are great for representing types of data and relationships, but sooner or later we're going to want to load or
+ * save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy}, which
+ * can be set directly on the Model:
+ *
+ * Ext.define('User', {
+ * extend: 'Ext.data.Model',
+ * fields: ['id', 'name', 'email'],
+ *
+ * proxy: {
+ * type: 'rest',
+ * url : '/users'
+ * }
+ * });
*
- * <p>A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain
- * a set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the
- * {@link Ext.data.Store Store docs} for more information on Stores.</p>
+ * Here we've set up a {@link Ext.data.proxy.Rest Rest Proxy}, which knows how to load and save data to and from a
+ * RESTful backend. Let's see how this works:
+ *
+ * var user = Ext.create('User', {name: 'Ed Spencer', email: 'ed@sencha.com'});
+ *
+ * user.save(); //POST /users
+ *
+ * Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this Model's
+ * data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't have an id,
+ * and performs the appropriate action - in this case issuing a POST request to the url we configured (/users). We
+ * configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full list.
+ *
+ * Loading data via the Proxy is equally easy:
+ *
+ * //get a reference to the User model class
+ * var User = Ext.ModelManager.getModel('User');
+ *
+ * //Uses the configured RestProxy to make a GET request to /users/123
+ * User.load(123, {
+ * success: function(user) {
+ * console.log(user.getId()); //logs 123
+ * }
+ * });
+ *
+ * Models can also be updated and destroyed easily:
+ *
+ * //the user Model we loaded in the last snippet:
+ * user.set('name', 'Edward Spencer');
+ *
+ * //tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
+ * user.save({
+ * success: function() {
+ * console.log('The User was updated');
+ * }
+ * });
+ *
+ * //tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
+ * user.destroy({
+ * success: function() {
+ * console.log('The User was destroyed!');
+ * }
+ * });
+ *
+ * # Usage in Stores
+ *
+ * It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this by
+ * creating a {@link Ext.data.Store Store}:
+ *
+ * var store = Ext.create('Ext.data.Store', {
+ * model: 'User'
+ * });
+ *
+ * //uses the Proxy we set up on Model to load the Store data
+ * store.load();
+ *
+ * A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain a
+ * set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the {@link
+ * Ext.data.Store Store docs} for more information on Stores.
*
* @constructor
+ * Creates new Model instance.
* @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
- * @param {Number} id Optional unique ID to assign to this model instance
+ * @param {Number} id (optional) Unique ID to assign to this model instance
*/
Ext.define('Ext.data.Model', {
alternateClassName: 'Ext.data.Record',
-
+
mixins: {
observable: 'Ext.util.Observable'
},
requires: [
'Ext.ModelManager',
+ 'Ext.data.IdGenerator',
'Ext.data.Field',
'Ext.data.Errors',
'Ext.data.Operation',
@@ -252,6 +250,7 @@ Ext.define('Ext.data.Model', {
associations = data.associations || [],
belongsTo = data.belongsTo,
hasMany = data.hasMany,
+ idgen = data.idgen,
fieldsMixedCollection = new Ext.util.MixedCollection(false, function(field) {
return field.name;
@@ -290,6 +289,10 @@ Ext.define('Ext.data.Model', {
data.fields = fieldsMixedCollection;
+ if (idgen) {
+ data.idgen = Ext.data.IdGenerator.get(idgen);
+ }
+
//associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
//we support that here
if (belongsTo) {
@@ -369,14 +372,17 @@ Ext.define('Ext.data.Model', {
// Fire the onModelDefined template method on ModelManager
Ext.ModelManager.onModelDefined(cls);
});
- }
+ };
},
inheritableStatics: {
- /**
- * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
+ /**
+ * Sets the Proxy to use for this model. Accepts any options that can be accepted by
+ * {@link Ext#createByAlias Ext.createByAlias}.
* @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
+ * @return {Ext.data.proxy.Proxy}
* @static
+ * @inheritable
*/
setProxy: function(proxy) {
//make sure we have an Ext.data.proxy.Proxy object
@@ -394,43 +400,45 @@ Ext.define('Ext.data.Model', {
return proxy;
},
- /**
+ /**
* Returns the configured Proxy for this Model
* @return {Ext.data.proxy.Proxy} The proxy
+ * @static
+ * @inheritable
*/
getProxy: function() {
return this.proxy;
},
- /**
- * <b>Static</b>. Asynchronously loads a model instance by id. Sample usage:
- <pre><code>
- MyApp.User = Ext.define('User', {
- extend: 'Ext.data.Model',
- fields: [
- {name: 'id', type: 'int'},
- {name: 'name', type: 'string'}
- ]
- });
-
- MyApp.User.load(10, {
- scope: this,
- failure: function(record, operation) {
- //do something if the load failed
- },
- success: function(record, operation) {
- //do something if the load succeeded
- },
- callback: function(record, operation) {
- //do something whether the load succeeded or failed
- }
- });
- </code></pre>
+ /**
+ * Asynchronously loads a model instance by id. Sample usage:
+ *
+ * MyApp.User = Ext.define('User', {
+ * extend: 'Ext.data.Model',
+ * fields: [
+ * {name: 'id', type: 'int'},
+ * {name: 'name', type: 'string'}
+ * ]
+ * });
+ *
+ * MyApp.User.load(10, {
+ * scope: this,
+ * failure: function(record, operation) {
+ * //do something if the load failed
+ * },
+ * success: function(record, operation) {
+ * //do something if the load succeeded
+ * },
+ * callback: function(record, operation) {
+ * //do something whether the load succeeded or failed
+ * }
+ * });
+ *
* @param {Number} id The id of the model to load
- * @param {Object} config Optional config object containing success, failure and callback functions, plus optional scope
- * @member Ext.data.Model
- * @method load
+ * @param {Object} config (optional) config object containing success, failure and callback functions, plus
+ * optional scope
* @static
+ * @inheritable
*/
load: function(id, config) {
config = Ext.apply({}, config);
@@ -465,18 +473,16 @@ Ext.define('Ext.data.Model', {
REJECT : 'reject',
COMMIT : 'commit',
- /**
- * Generates a sequential id. This method is typically called when a record is {@link #create}d
- * and {@link #Record no id has been specified}. The id will automatically be assigned
- * to the record. The returned id takes the form:
- * <tt>{PREFIX}-{AUTO_ID}</tt>.<div class="mdetail-params"><ul>
- * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.PREFIX</tt>
- * (defaults to <tt>'ext-record'</tt>)</p></li>
- * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.AUTO_ID</tt>
- * (defaults to <tt>1</tt> initially)</p></li>
- * </ul></div>
- * @param {Ext.data.Model} rec The record being created. The record does not exist, it's a {@link #phantom}.
- * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
+ /**
+ * Generates a sequential id. This method is typically called when a record is {@link Ext#create
+ * create}d and {@link #constructor no id has been specified}. The id will automatically be assigned to the
+ * record. The returned id takes the form: {PREFIX}-{AUTO_ID}.
+ *
+ * - **PREFIX** : String - Ext.data.Model.PREFIX (defaults to 'ext-record')
+ * - **AUTO_ID** : String - Ext.data.Model.AUTO_ID (defaults to 1 initially)
+ *
+ * @param {Ext.data.Model} rec The record being created. The record does not exist, it's a {@link #phantom}.
+ * @return {String} auto-generated string id, `"ext-record-i++"`;
* @static
*/
id: function(rec) {
@@ -486,69 +492,176 @@ Ext.define('Ext.data.Model', {
return id;
}
},
-
- /**
- * Internal flag used to track whether or not the model instance is currently being edited. Read-only
- * @property editing
- * @type Boolean
+
+ /**
+ * @cfg {String/Object} idgen
+ * The id generator to use for this model. The default id generator does not generate
+ * values for the {@link #idProperty}.
+ *
+ * This can be overridden at the model level to provide a custom generator for a model.
+ * The simplest form of this would be:
+ *
+ * Ext.define('MyApp.data.MyModel', {
+ * extend: 'Ext.data.Model',
+ * requires: ['Ext.data.SequentialIdGenerator'],
+ * idgen: 'sequential',
+ * ...
+ * });
+ *
+ * The above would generate {@link Ext.data.SequentialIdGenerator sequential} id's such
+ * as 1, 2, 3 etc..
+ *
+ * Another useful id generator is {@link Ext.data.UuidGenerator}:
+ *
+ * Ext.define('MyApp.data.MyModel', {
+ * extend: 'Ext.data.Model',
+ * requires: ['Ext.data.UuidGenerator'],
+ * idgen: 'uuid',
+ * ...
+ * });
+ *
+ * An id generation can also be further configured:
+ *
+ * Ext.define('MyApp.data.MyModel', {
+ * extend: 'Ext.data.Model',
+ * idgen: {
+ * type: 'sequential',
+ * seed: 1000,
+ * prefix: 'ID_'
+ * }
+ * });
+ *
+ * The above would generate id's such as ID_1000, ID_1001, ID_1002 etc..
+ *
+ * If multiple models share an id space, a single generator can be shared:
+ *
+ * Ext.define('MyApp.data.MyModelX', {
+ * extend: 'Ext.data.Model',
+ * idgen: {
+ * type: 'sequential',
+ * id: 'xy'
+ * }
+ * });
+ *
+ * Ext.define('MyApp.data.MyModelY', {
+ * extend: 'Ext.data.Model',
+ * idgen: {
+ * type: 'sequential',
+ * id: 'xy'
+ * }
+ * });
+ *
+ * For more complex, shared id generators, a custom generator is the best approach.
+ * See {@link Ext.data.IdGenerator} for details on creating custom id generators.
+ *
+ * @markdown
+ */
+ idgen: {
+ isGenerator: true,
+ type: 'default',
+
+ generate: function () {
+ return null;
+ },
+ getRecId: function (rec) {
+ return rec.modelName + '-' + rec.internalId;
+ }
+ },
+
+ /**
+ * @property {Boolean} editing
+ * Internal flag used to track whether or not the model instance is currently being edited. Read-only.
*/
editing : false,
- /**
- * Readonly flag - true if this Record has been modified.
- * @type Boolean
+ /**
+ * @property {Boolean} dirty
+ * True if this Record has been modified. Read-only.
*/
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.)
+ /**
+ * @cfg {String} persistenceProperty
+ * 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',
+ persistenceProperty: 'data',
evented: false,
isModel: true,
- /**
- * <tt>true</tt> when the record does not yet exist in a server-side database (see
- * {@link #setDirty}). Any record which has a real database pk set as its id property
- * is NOT a phantom -- it's real.
- * @property phantom
- * @type {Boolean}
+ /**
+ * @property {Boolean} phantom
+ * True when the record does not yet exist in a server-side database (see {@link #setDirty}).
+ * Any record which has a real database pk set as its id property is NOT a phantom -- it's real.
*/
phantom : false,
- /**
- * @cfg {String} idProperty The name of the field treated as this Model's unique id (defaults to 'id').
+ /**
+ * @cfg {String} idProperty
+ * The name of the field treated as this Model's unique id. Defaults to 'id'.
*/
idProperty: 'id',
- /**
- * The string type of the default Model Proxy. Defaults to 'ajax'
- * @property defaultProxyType
- * @type String
+ /**
+ * @cfg {String} defaultProxyType
+ * The string type of the default Model Proxy. Defaults to 'ajax'.
*/
defaultProxyType: 'ajax',
- /**
- * An array of the fields defined on this model
- * @property fields
- * @type {Array}
+ // Fields config and property
+ /**
+ * @cfg {Object[]/String[]} fields
+ * The fields for this model.
+ */
+ /**
+ * @property {Ext.util.MixedCollection} fields
+ * The fields defined on this model.
*/
- constructor: function(data, id) {
+ /**
+ * @cfg {Object[]} validations
+ * An array of {@link Ext.data.validations validations} for this model.
+ */
+
+ // Associations configs and properties
+ /**
+ * @cfg {Object[]} associations
+ * An array of {@link Ext.data.Association associations} for this model.
+ */
+ /**
+ * @cfg {String/Object/String[]/Object[]} hasMany
+ * One or more {@link Ext.data.HasManyAssociation HasMany associations} for this model.
+ */
+ /**
+ * @cfg {String/Object/String[]/Object[]} belongsTo
+ * One or more {@link Ext.data.BelongsToAssociation BelongsTo associations} for this model.
+ */
+ /**
+ * @property {Ext.util.MixedCollection} associations
+ * {@link Ext.data.Association Associations} defined on this model.
+ */
+
+ /**
+ * @cfg {String/Object/Ext.data.proxy.Proxy} proxy
+ * The {@link Ext.data.proxy.Proxy proxy} to use for this model.
+ */
+
+ // raw not documented intentionally, meant to be used internally.
+ constructor: function(data, id, raw) {
data = data || {};
-
+
var me = this,
fields,
length,
field,
name,
i,
+ newId,
isArray = Ext.isArray(data),
newData = isArray ? {} : null; // to hold mapped array data if needed
- /**
+ /**
* An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
* @property internalId
* @type String
@@ -556,18 +669,30 @@ Ext.define('Ext.data.Model', {
*/
me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);
+ /**
+ * @property {Object} raw The raw data used to create this model if created via a reader.
+ */
+ me.raw = raw;
+
Ext.applyIf(me, {
- data: {}
+ data: {}
});
-
- /**
- * Key: value pairs of all fields whose values have changed
- * @property modified
- * @type Object
+
+ /**
+ * @property {Object} modified Key: value pairs of all fields whose values have changed
*/
me.modified = {};
- me[me.persistanceProperty] = {};
+ // Deal with spelling error in previous releases
+ if (me.persistanceProperty) {
+ //<debug>
+ if (Ext.isDefined(Ext.global.console)) {
+ Ext.global.console.warn('Ext.data.Model: persistanceProperty has been deprecated. Use persistenceProperty instead.');
+ }
+ //</debug>
+ me.persistenceProperty = me.persistanceProperty;
+ }
+ me[me.persistenceProperty] = {};
me.mixins.observable.constructor.call(me);
@@ -579,7 +704,7 @@ Ext.define('Ext.data.Model', {
field = fields[i];
name = field.name;
- if (isArray){
+ if (isArray){
// Have to map array data so the values get assigned to the named fields
// rather than getting set as the field names with undefined values.
newData[name] = data[i];
@@ -590,52 +715,58 @@ Ext.define('Ext.data.Model', {
}
me.set(newData || data);
- // clear any dirty/modified since we're initializing
- me.dirty = false;
- me.modified = {};
if (me.getId()) {
me.phantom = false;
+ } else if (me.phantom) {
+ newId = me.idgen.generate();
+ if (newId !== null) {
+ me.setId(newId);
+ }
}
+ // clear any dirty/modified since we're initializing
+ me.dirty = false;
+ me.modified = {};
+
if (typeof me.init == 'function') {
me.init();
}
- me.id = me.modelName + '-' + me.internalId;
-
- Ext.ModelManager.register(me);
+ me.id = me.idgen.getRecId(me);
},
-
- /**
+
+ /**
* Returns the value of the given field
* @param {String} fieldName The field to fetch the value for
- * @return {Mixed} The value
+ * @return {Object} The value
*/
get: function(field) {
- return this[this.persistanceProperty][field];
+ return this[this.persistenceProperty][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
+ * @param {String/Object} fieldName The field to set, or an object containing key/value pairs
+ * @param {Object} value The value to set
*/
set: function(fieldName, value) {
var me = this,
fields = me.fields,
modified = me.modified,
convertFields = [],
- field, key, i, currentValue;
+ field, key, i, currentValue, notEditing, count, length;
/*
* 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)) {
+ notEditing = !me.editing;
+ count = 0;
for (key in fieldName) {
if (fieldName.hasOwnProperty(key)) {
-
+
//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);
@@ -643,16 +774,30 @@ Ext.define('Ext.data.Model', {
convertFields.push(key);
continue;
}
-
+
+ if (!count && notEditing) {
+ me.beginEdit();
+ }
+ ++count;
me.set(key, fieldName[key]);
}
}
- for (i = 0; i < convertFields.length; i++) {
- field = convertFields[i];
- me.set(field, fieldName[field]);
+ length = convertFields.length;
+ if (length) {
+ if (!count && notEditing) {
+ me.beginEdit();
+ }
+ count += length;
+ for (i = 0; i < length; i++) {
+ field = convertFields[i];
+ me.set(field, fieldName[field]);
+ }
}
+ if (notEditing && count) {
+ me.endEdit();
+ }
} else {
if (fields) {
field = fields.get(fieldName);
@@ -662,11 +807,28 @@ Ext.define('Ext.data.Model', {
}
}
currentValue = me.get(fieldName);
- me[me.persistanceProperty][fieldName] = value;
-
+ me[me.persistenceProperty][fieldName] = value;
+
if (field && field.persist && !me.isEqual(currentValue, value)) {
- me.dirty = true;
- me.modified[fieldName] = currentValue;
+ if (me.isModified(fieldName)) {
+ if (me.isEqual(modified[fieldName], value)) {
+ // the original value in me.modified equals the new value, so the
+ // field is no longer modified
+ delete modified[fieldName];
+ // we might have removed the last modified field, so check to see if
+ // there are any modified fields remaining and correct me.dirty:
+ me.dirty = false;
+ for (key in modified) {
+ if (modified.hasOwnProperty(key)){
+ me.dirty = true;
+ break;
+ }
+ }
+ }
+ } else {
+ me.dirty = true;
+ modified[fieldName] = currentValue;
+ }
}
if (!me.editing) {
@@ -674,8 +836,8 @@ Ext.define('Ext.data.Model', {
}
}
},
-
- /**
+
+ /**
* Checks if two values are equal, taking into account certain
* special factors, for example dates.
* @private
@@ -689,23 +851,22 @@ Ext.define('Ext.data.Model', {
}
return a === b;
},
-
- /**
- * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
- * are relayed to the containing store. When an edit has begun, it must be followed
- * by either {@link #endEdit} or {@link #cancelEdit}.
+
+ /**
+ * Begins an edit. While in edit mode, no events (e.g.. the `update` event) are relayed to the containing store.
+ * When an edit has begun, it must be followed by either {@link #endEdit} or {@link #cancelEdit}.
*/
beginEdit : function(){
var me = this;
if (!me.editing) {
me.editing = true;
me.dirtySave = me.dirty;
- me.dataSave = Ext.apply({}, me[me.persistanceProperty]);
+ me.dataSave = Ext.apply({}, me[me.persistenceProperty]);
me.modifiedSave = Ext.apply({}, me.modified);
}
},
-
- /**
+
+ /**
* Cancels all changes made in the current edit operation.
*/
cancelEdit : function(){
@@ -714,35 +875,61 @@ Ext.define('Ext.data.Model', {
me.editing = false;
// reset the modified state, nothing changed since the edit began
me.modified = me.modifiedSave;
- me[me.persistanceProperty] = me.dataSave;
+ me[me.persistenceProperty] = me.dataSave;
me.dirty = me.dirtySave;
delete me.modifiedSave;
delete me.dataSave;
delete me.dirtySave;
}
},
-
- /**
- * End an edit. If any data was modified, the containing store is notified
- * (ie, the store's <code>update</code> event will fire).
+
+ /**
+ * Ends an edit. If any data was modified, the containing store is notified (ie, the store's `update` event will
+ * fire).
* @param {Boolean} silent True to not notify the store of the change
*/
endEdit : function(silent){
- var me = this;
+ var me = this,
+ didChange;
+
if (me.editing) {
me.editing = false;
+ didChange = me.dirty || me.changedWhileEditing();
delete me.modifiedSave;
delete me.dataSave;
delete me.dirtySave;
- if (silent !== true && me.dirty) {
+ if (silent !== true && didChange) {
me.afterEdit();
}
}
},
- /**
+ /**
+ * Checks if the underlying data has changed during an edit. This doesn't necessarily
+ * mean the record is dirty, however we still need to notify the store since it may need
+ * to update any views.
+ * @private
+ * @return {Boolean} True if the underlying data has changed during an edit.
+ */
+ changedWhileEditing: function(){
+ var me = this,
+ saved = me.dataSave,
+ data = me[me.persistenceProperty],
+ key;
+
+ for (key in data) {
+ if (data.hasOwnProperty(key)) {
+ if (!me.isEqual(data[key], saved[key])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+
+ /**
* Gets a hash of only the fields that have been modified since this Model was created or commited.
- * @return Object
+ * @return {Object}
*/
getChanges : function(){
var modified = this.modified,
@@ -757,30 +944,27 @@ Ext.define('Ext.data.Model', {
return changes;
},
-
- /**
- * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
- * since the load or last commit.
+
+ /**
+ * 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.hasOwnProperty(fieldName);
},
-
- /**
- * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>. This method
- * is used interally when adding <code>{@link #phantom}</code> records to a
- * {@link Ext.data.Store#writer writer enabled store}.</p>
- * <br><p>Marking a record <code>{@link #dirty}</code> 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.</p>
+
+ /**
+ * Marks this **Record** as `{@link #dirty}`. This method is used interally when adding `{@link #phantom}` records
+ * to a {@link Ext.data.proxy.Server#writer writer enabled store}.
+ *
+ * Marking a record `{@link #dirty}` causes the phantom to be returned by {@link Ext.data.Store#getUpdatedRecords}
+ * where it will have a create action composed for it during {@link Ext.data.Model#save model save} operations.
*/
setDirty : function() {
var me = this,
name;
-
+
me.dirty = true;
me.fields.each(function(field) {
@@ -799,15 +983,17 @@ Ext.define('Ext.data.Model', {
return this.setDirty.apply(this, arguments);
},
//</debug>
-
- /**
- * 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.
- * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
- * to have their code notified of reject operations.</p>
- * @param {Boolean} silent (optional) True to skip notification of the owning
- * store of the change (defaults to false)
+
+ /**
+ * 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 me = this,
@@ -817,7 +1003,7 @@ Ext.define('Ext.data.Model', {
for (field in modified) {
if (modified.hasOwnProperty(field)) {
if (typeof modified[field] != "function") {
- me[me.persistanceProperty][field] = modified[field];
+ me[me.persistenceProperty][field] = modified[field];
}
}
}
@@ -831,20 +1017,20 @@ Ext.define('Ext.data.Model', {
}
},
- /**
- * 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.
- * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
- * to have their code notified of commit operations.</p>
- * @param {Boolean} silent (optional) True to skip notification of the owning
- * store of the change (defaults to false)
+ /**
+ * 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) {
var me = this;
-
- me.dirty = false;
- me.editing = false;
+ me.phantom = me.dirty = me.editing = false;
me.modified = {};
if (silent !== true) {
@@ -852,26 +1038,29 @@ Ext.define('Ext.data.Model', {
}
},
- /**
+ /**
* 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 <code>{@link #id}</code>.
- * To generate a phantom instance with a new id use:<pre><code>
-var rec = record.copy(); // clone the record
-Ext.data.Model.id(rec); // automatically generate a unique sequential id
- * </code></pre>
- * @return {Record}
+ *
+ * @param {String} [id] A new id, defaults to the id of the instance being copied.
+ * See `{@link Ext.data.Model#id 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 {Ext.data.Model}
*/
copy : function(newId) {
var me = this;
-
- return new me.self(Ext.apply({}, me[me.persistanceProperty]), newId || me.internalId);
+
+ return new me.self(Ext.apply({}, me[me.persistenceProperty]), newId || me.internalId);
},
- /**
- * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
+ /**
+ * Sets the Proxy to use for this model. Accepts any options that can be accepted by
+ * {@link Ext#createByAlias Ext.createByAlias}.
+ *
* @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
- * @static
+ * @return {Ext.data.proxy.Proxy}
*/
setProxy: function(proxy) {
//make sure we have an Ext.data.proxy.Proxy object
@@ -889,17 +1078,16 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
return proxy;
},
- /**
- * Returns the configured Proxy for this Model
+ /**
+ * Returns the configured Proxy for this Model.
* @return {Ext.data.proxy.Proxy} The proxy
*/
getProxy: function() {
return this.proxy;
},
- /**
- * Validates the current data against all of its configured {@link #validations} and returns an
- * {@link Ext.data.Errors Errors} object
+ /**
+ * Validates the current data against all of its configured {@link #validations}.
* @return {Ext.data.Errors} The errors object
*/
validate: function() {
@@ -929,7 +1117,7 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
return errors;
},
- /**
+ /**
* Checks if the model is valid. See {@link #validate}.
* @return {Boolean} True if the model is valid.
*/
@@ -937,9 +1125,9 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
return this.validate().isValid();
},
- /**
- * Saves the model instance using the configured proxy
- * @param {Object} options Options to pass to the proxy
+ /**
+ * Saves the model instance using the configured proxy.
+ * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
* @return {Ext.data.Model} The Model instance
*/
save: function(options) {
@@ -980,9 +1168,9 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
return me;
},
- /**
- * Destroys the model using the configured proxy
- * @param {Object} options Options to pass to the proxy
+ /**
+ * Destroys the model using the configured proxy.
+ * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
* @return {Ext.data.Model} The Model instance
*/
destroy: function(options){
@@ -1013,43 +1201,43 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
return me;
},
- /**
- * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}
+ /**
+ * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}.
* @return {Number} The id
*/
getId: function() {
return this.get(this.idProperty);
},
- /**
- * Sets the model instance's id field to the given id
+ /**
+ * Sets the model instance's id field to the given id.
* @param {Number} id The new id
*/
setId: function(id) {
this.set(this.idProperty, id);
},
- /**
- * Tells this model instance that it has been added to a store
- * @param {Ext.data.Store} store The store that the model has been added to
+ /**
+ * Tells this model instance that it has been added to a store.
+ * @param {Ext.data.Store} store The store to which this model has been added.
*/
join : function(store) {
- /**
- * The {@link Ext.data.Store} to which this Record belongs.
- * @property store
- * @type {Ext.data.Store}
+ /**
+ * @property {Ext.data.Store} store
+ * The {@link Ext.data.Store Store} to which this Record belongs.
*/
this.store = store;
},
- /**
- * Tells this model instance that it has been removed from the store
+ /**
+ * Tells this model instance that it has been removed from the store.
+ * @param {Ext.data.Store} store The store from which this model has been removed.
*/
- unjoin: function() {
+ unjoin: function(store) {
delete this.store;
},
- /**
+ /**
* @private
* If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
* afterEdit method is called
@@ -1058,7 +1246,7 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
this.callStore('afterEdit');
},
- /**
+ /**
* @private
* If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
* afterReject method is called
@@ -1067,7 +1255,7 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
this.callStore("afterReject");
},
- /**
+ /**
* @private
* If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
* afterCommit method is called
@@ -1076,7 +1264,7 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
this.callStore('afterCommit');
},
- /**
+ /**
* @private
* Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
* {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
@@ -1091,33 +1279,34 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
}
},
- /**
- * Gets all of the data from this Models *loaded* associations.
- * It does this recursively - for example if we have a User which
- * hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
- * {
- * orders: [
- * {
- * id: 123,
- * status: 'shipped',
- * orderItems: [
- * ...
- * ]
- * }
- * ]
- * }
+ /**
+ * Gets all of the data from this Models *loaded* associations. It does this recursively - for example if we have a
+ * User which hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
+ *
+ * {
+ * orders: [
+ * {
+ * id: 123,
+ * status: 'shipped',
+ * orderItems: [
+ * ...
+ * ]
+ * }
+ * ]
+ * }
+ *
* @return {Object} The nested data set for the Model's loaded associations
*/
getAssociatedData: function(){
return this.prepareAssociatedData(this, [], null);
},
- /**
+ /**
* @private
* This complex-looking method takes a given Model instance and returns an object containing all data from
* all of that Model's *loaded* associations. See (@link #getAssociatedData}
* @param {Ext.data.Model} record The Model instance
- * @param {Array} ids PRIVATE. The set of Model instance internalIds that have already been loaded
+ * @param {String[]} ids PRIVATE. The set of Model instance internalIds that have already been loaded
* @param {String} associationType (optional) The name of the type of association to limit to.
* @return {Object} The nested data set for the Model's loaded associations
*/
@@ -1181,4 +1370,6 @@ Ext.data.Model.id(rec); // automatically generate a unique sequential id
return associationData;
}
});
-
\ No newline at end of file
+
+
+