4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-data-Model-method-constructor'><span id='Ext-data-Model'>/**
19 </span></span> * @author Ed Spencer
21 * A Model represents some object that your application manages. For example, one might define a Model for Users,
22 * Products, Cars, or any other real-world object that we want to model in the system. Models are registered via the
23 * {@link Ext.ModelManager model manager}, and are used by {@link Ext.data.Store stores}, which are in turn used by many
24 * of the data-bound components in Ext.
26 * Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:
28 * Ext.define('User', {
29 * extend: 'Ext.data.Model',
31 * {name: 'name', type: 'string'},
32 * {name: 'age', type: 'int'},
33 * {name: 'phone', type: 'string'},
34 * {name: 'alive', type: 'boolean', defaultValue: true}
37 * changeName: function() {
38 * var oldName = this.get('name'),
39 * newName = oldName + " The Barbarian";
41 * this.set('name', newName);
45 * The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link
46 * Ext.ModelManager ModelManager}, and all other functions and properties are copied to the new Model's prototype.
48 * Now we can create instances of our User model and call any model logic we defined:
50 * var user = Ext.create('User', {
53 * phone: '555-555-5555'
57 * user.get('name'); //returns "Conan The Barbarian"
61 * Models have built-in support for validations, which are executed against the validator functions in {@link
62 * Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to
65 * Ext.define('User', {
66 * extend: 'Ext.data.Model',
68 * {name: 'name', type: 'string'},
69 * {name: 'age', type: 'int'},
70 * {name: 'phone', type: 'string'},
71 * {name: 'gender', type: 'string'},
72 * {name: 'username', type: 'string'},
73 * {name: 'alive', type: 'boolean', defaultValue: true}
77 * {type: 'presence', field: 'age'},
78 * {type: 'length', field: 'name', min: 2},
79 * {type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
80 * {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
81 * {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
85 * The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
88 * var instance = Ext.create('User', {
91 * username: 'edspencer'
94 * var errors = instance.validate();
98 * Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and {@link
99 * Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
100 * application which deals with Users, Posts and Comments. We can express the relationships between these models like
103 * Ext.define('Post', {
104 * extend: 'Ext.data.Model',
105 * fields: ['id', 'user_id'],
108 * hasMany : {model: 'Comment', name: 'comments'}
111 * Ext.define('Comment', {
112 * extend: 'Ext.data.Model',
113 * fields: ['id', 'user_id', 'post_id'],
118 * Ext.define('User', {
119 * extend: 'Ext.data.Model',
124 * {model: 'Comment', name: 'comments'}
128 * See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the
129 * usage and configuration of associations. Note that associations can also be specified like this:
131 * Ext.define('User', {
132 * extend: 'Ext.data.Model',
136 * {type: 'hasMany', model: 'Post', name: 'posts'},
137 * {type: 'hasMany', model: 'Comment', name: 'comments'}
143 * Models are great for representing types of data and relationships, but sooner or later we're going to want to load or
144 * save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy}, which
145 * can be set directly on the Model:
147 * Ext.define('User', {
148 * extend: 'Ext.data.Model',
149 * fields: ['id', 'name', 'email'],
157 * 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
158 * RESTful backend. Let's see how this works:
160 * var user = Ext.create('User', {name: 'Ed Spencer', email: 'ed@sencha.com'});
162 * user.save(); //POST /users
164 * Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this Model's
165 * data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't have an id,
166 * and performs the appropriate action - in this case issuing a POST request to the url we configured (/users). We
167 * configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full list.
169 * Loading data via the Proxy is equally easy:
171 * //get a reference to the User model class
172 * var User = Ext.ModelManager.getModel('User');
174 * //Uses the configured RestProxy to make a GET request to /users/123
176 * success: function(user) {
177 * console.log(user.getId()); //logs 123
181 * Models can also be updated and destroyed easily:
183 * //the user Model we loaded in the last snippet:
184 * user.set('name', 'Edward Spencer');
186 * //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
188 * success: function() {
189 * console.log('The User was updated');
193 * //tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
195 * success: function() {
196 * console.log('The User was destroyed!');
202 * 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
203 * creating a {@link Ext.data.Store Store}:
205 * var store = Ext.create('Ext.data.Store', {
209 * //uses the Proxy we set up on Model to load the Store data
212 * A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain a
213 * set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the {@link
214 * Ext.data.Store Store docs} for more information on Stores.
217 * Creates new Model instance.
218 * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
219 * @param {Number} id (optional) Unique ID to assign to this model instance
221 Ext.define('Ext.data.Model', {
222 alternateClassName: 'Ext.data.Record',
225 observable: 'Ext.util.Observable'
230 'Ext.data.IdGenerator',
233 'Ext.data.Operation',
234 'Ext.data.validations',
235 'Ext.data.proxy.Ajax',
236 'Ext.util.MixedCollection'
239 onClassExtended: function(cls, data) {
240 var onBeforeClassCreated = data.onBeforeClassCreated;
242 data.onBeforeClassCreated = function(cls, data) {
244 name = Ext.getClassName(cls),
245 prototype = cls.prototype,
246 superCls = cls.prototype.superclass,
248 validations = data.validations || [],
249 fields = data.fields || [],
250 associations = data.associations || [],
251 belongsTo = data.belongsTo,
252 hasMany = data.hasMany,
255 fieldsMixedCollection = new Ext.util.MixedCollection(false, function(field) {
259 associationsMixedCollection = new Ext.util.MixedCollection(false, function(association) {
260 return association.name;
263 superValidations = superCls.validations,
264 superFields = superCls.fields,
265 superAssociations = superCls.associations,
270 // Save modelName on class and its prototype
271 cls.modelName = name;
272 prototype.modelName = name;
274 // Merge the validations of the superclass and the new subclass
275 if (superValidations) {
276 validations = superValidations.concat(validations);
279 data.validations = validations;
281 // Merge the fields of the superclass and the new subclass
283 fields = superFields.items.concat(fields);
286 for (i = 0, ln = fields.length; i < ln; ++i) {
287 fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
290 data.fields = fieldsMixedCollection;
293 data.idgen = Ext.data.IdGenerator.get(idgen);
296 //associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
297 //we support that here
299 belongsTo = Ext.Array.from(belongsTo);
301 for (i = 0, ln = belongsTo.length; i < ln; ++i) {
302 association = belongsTo[i];
304 if (!Ext.isObject(association)) {
305 association = {model: association};
308 association.type = 'belongsTo';
309 associations.push(association);
312 delete data.belongsTo;
316 hasMany = Ext.Array.from(hasMany);
317 for (i = 0, ln = hasMany.length; i < ln; ++i) {
318 association = hasMany[i];
320 if (!Ext.isObject(association)) {
321 association = {model: association};
324 association.type = 'hasMany';
325 associations.push(association);
331 if (superAssociations) {
332 associations = superAssociations.items.concat(associations);
335 for (i = 0, ln = associations.length; i < ln; ++i) {
336 dependencies.push('association.' + associations[i].type.toLowerCase());
340 if (typeof data.proxy === 'string') {
341 dependencies.push('proxy.' + data.proxy);
343 else if (typeof data.proxy.type === 'string') {
344 dependencies.push('proxy.' + data.proxy.type);
348 Ext.require(dependencies, function() {
349 Ext.ModelManager.registerType(name, cls);
351 for (i = 0, ln = associations.length; i < ln; ++i) {
352 association = associations[i];
354 Ext.apply(association, {
356 associatedModel: association.model
359 if (Ext.ModelManager.getModel(association.model) === undefined) {
360 Ext.ModelManager.registerDeferredAssociation(association);
362 associationsMixedCollection.add(Ext.data.Association.create(association));
366 data.associations = associationsMixedCollection;
368 onBeforeClassCreated.call(me, cls, data);
370 cls.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);
372 // Fire the onModelDefined template method on ModelManager
373 Ext.ModelManager.onModelDefined(cls);
378 inheritableStatics: {
379 <span id='Ext-data-Model-static-method-setProxy'> /**
380 </span> * Sets the Proxy to use for this model. Accepts any options that can be accepted by
381 * {@link Ext#createByAlias Ext.createByAlias}.
382 * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
383 * @return {Ext.data.proxy.Proxy}
387 setProxy: function(proxy) {
388 //make sure we have an Ext.data.proxy.Proxy object
389 if (!proxy.isProxy) {
390 if (typeof proxy == "string") {
395 proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
397 proxy.setModel(this);
398 this.proxy = this.prototype.proxy = proxy;
403 <span id='Ext-data-Model-static-method-getProxy'> /**
404 </span> * Returns the configured Proxy for this Model
405 * @return {Ext.data.proxy.Proxy} The proxy
409 getProxy: function() {
413 <span id='Ext-data-Model-static-method-load'> /**
414 </span> * Asynchronously loads a model instance by id. Sample usage:
416 * MyApp.User = Ext.define('User', {
417 * extend: 'Ext.data.Model',
419 * {name: 'id', type: 'int'},
420 * {name: 'name', type: 'string'}
424 * MyApp.User.load(10, {
426 * failure: function(record, operation) {
427 * //do something if the load failed
429 * success: function(record, operation) {
430 * //do something if the load succeeded
432 * callback: function(record, operation) {
433 * //do something whether the load succeeded or failed
437 * @param {Number} id The id of the model to load
438 * @param {Object} config (optional) config object containing success, failure and callback functions, plus
443 load: function(id, config) {
444 config = Ext.apply({}, config);
445 config = Ext.applyIf(config, {
450 var operation = Ext.create('Ext.data.Operation', config),
451 scope = config.scope || this,
455 callback = function(operation) {
456 if (operation.wasSuccessful()) {
457 record = operation.getRecords()[0];
458 Ext.callback(config.success, scope, [record, operation]);
460 Ext.callback(config.failure, scope, [record, operation]);
462 Ext.callback(config.callback, scope, [record, operation]);
465 this.proxy.read(operation, callback, this);
470 PREFIX : 'ext-record',
476 <span id='Ext-data-Model-static-method-id'> /**
477 </span> * Generates a sequential id. This method is typically called when a record is {@link Ext#create
478 * create}d and {@link #constructor no id has been specified}. The id will automatically be assigned to the
479 * record. The returned id takes the form: {PREFIX}-{AUTO_ID}.
481 * - **PREFIX** : String - Ext.data.Model.PREFIX (defaults to 'ext-record')
482 * - **AUTO_ID** : String - Ext.data.Model.AUTO_ID (defaults to 1 initially)
484 * @param {Ext.data.Model} rec The record being created. The record does not exist, it's a {@link #phantom}.
485 * @return {String} auto-generated string id, `"ext-record-i++"`;
489 var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
496 <span id='Ext-data-Model-cfg-idgen'> /**
497 </span> * @cfg {String/Object} idgen
498 * The id generator to use for this model. The default id generator does not generate
499 * values for the {@link #idProperty}.
501 * This can be overridden at the model level to provide a custom generator for a model.
502 * The simplest form of this would be:
504 * Ext.define('MyApp.data.MyModel', {
505 * extend: 'Ext.data.Model',
506 * requires: ['Ext.data.SequentialIdGenerator'],
507 * idgen: 'sequential',
511 * The above would generate {@link Ext.data.SequentialIdGenerator sequential} id's such
514 * Another useful id generator is {@link Ext.data.UuidGenerator}:
516 * Ext.define('MyApp.data.MyModel', {
517 * extend: 'Ext.data.Model',
518 * requires: ['Ext.data.UuidGenerator'],
523 * An id generation can also be further configured:
525 * Ext.define('MyApp.data.MyModel', {
526 * extend: 'Ext.data.Model',
528 * type: 'sequential',
534 * The above would generate id's such as ID_1000, ID_1001, ID_1002 etc..
536 * If multiple models share an id space, a single generator can be shared:
538 * Ext.define('MyApp.data.MyModelX', {
539 * extend: 'Ext.data.Model',
541 * type: 'sequential',
546 * Ext.define('MyApp.data.MyModelY', {
547 * extend: 'Ext.data.Model',
549 * type: 'sequential',
554 * For more complex, shared id generators, a custom generator is the best approach.
555 * See {@link Ext.data.IdGenerator} for details on creating custom id generators.
563 generate: function () {
566 getRecId: function (rec) {
567 return rec.modelName + '-' + rec.internalId;
571 <span id='Ext-data-Model-property-editing'> /**
572 </span> * @property {Boolean} editing
573 * Internal flag used to track whether or not the model instance is currently being edited. Read-only.
577 <span id='Ext-data-Model-property-dirty'> /**
578 </span> * @property {Boolean} dirty
579 * True if this Record has been modified. Read-only.
583 <span id='Ext-data-Model-cfg-persistenceProperty'> /**
584 </span> * @cfg {String} persistenceProperty
585 * The property on this Persistable object that its data is saved to. Defaults to 'data'
586 * (e.g. all persistable data resides in this.data.)
588 persistenceProperty: 'data',
593 <span id='Ext-data-Model-property-phantom'> /**
594 </span> * @property {Boolean} phantom
595 * True when the record does not yet exist in a server-side database (see {@link #setDirty}).
596 * Any record which has a real database pk set as its id property is NOT a phantom -- it's real.
600 <span id='Ext-data-Model-cfg-idProperty'> /**
601 </span> * @cfg {String} idProperty
602 * The name of the field treated as this Model's unique id. Defaults to 'id'.
606 <span id='Ext-data-Model-cfg-defaultProxyType'> /**
607 </span> * @cfg {String} defaultProxyType
608 * The string type of the default Model Proxy. Defaults to 'ajax'.
610 defaultProxyType: 'ajax',
612 // Fields config and property
613 <span id='Ext-data-Model-cfg-fields'> /**
614 </span> * @cfg {Object[]/String[]} fields
615 * The fields for this model.
617 <span id='Ext-data-Model-property-fields'> /**
618 </span> * @property {Ext.util.MixedCollection} fields
619 * The fields defined on this model.
622 <span id='Ext-data-Model-cfg-validations'> /**
623 </span> * @cfg {Object[]} validations
624 * An array of {@link Ext.data.validations validations} for this model.
627 // Associations configs and properties
628 <span id='Ext-data-Model-cfg-associations'> /**
629 </span> * @cfg {Object[]} associations
630 * An array of {@link Ext.data.Association associations} for this model.
632 <span id='Ext-data-Model-cfg-hasMany'> /**
633 </span> * @cfg {String/Object/String[]/Object[]} hasMany
634 * One or more {@link Ext.data.HasManyAssociation HasMany associations} for this model.
636 <span id='Ext-data-Model-cfg-belongsTo'> /**
637 </span> * @cfg {String/Object/String[]/Object[]} belongsTo
638 * One or more {@link Ext.data.BelongsToAssociation BelongsTo associations} for this model.
640 <span id='Ext-data-Model-property-associations'> /**
641 </span> * @property {Ext.util.MixedCollection} associations
642 * {@link Ext.data.Association Associations} defined on this model.
645 <span id='Ext-data-Model-cfg-proxy'> /**
646 </span> * @cfg {String/Object/Ext.data.proxy.Proxy} proxy
647 * The {@link Ext.data.proxy.Proxy proxy} to use for this model.
650 // raw not documented intentionally, meant to be used internally.
651 constructor: function(data, id, raw) {
661 isArray = Ext.isArray(data),
662 newData = isArray ? {} : null; // to hold mapped array data if needed
664 <span id='Ext-data-Model-property-internalId'> /**
665 </span> * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
666 * @property internalId
670 me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);
672 <span id='Ext-data-Model-property-raw'> /**
673 </span> * @property {Object} raw The raw data used to create this model if created via a reader.
681 <span id='Ext-data-Model-property-modified'> /**
682 </span> * @property {Object} modified Key: value pairs of all fields whose values have changed
686 // Deal with spelling error in previous releases
687 if (me.persistanceProperty) {
689 if (Ext.isDefined(Ext.global.console)) {
690 Ext.global.console.warn('Ext.data.Model: persistanceProperty has been deprecated. Use persistenceProperty instead.');
693 me.persistenceProperty = me.persistanceProperty;
695 me[me.persistenceProperty] = {};
697 me.mixins.observable.constructor.call(me);
699 //add default field values if present
700 fields = me.fields.items;
701 length = fields.length;
703 for (i = 0; i < length; i++) {
708 // Have to map array data so the values get assigned to the named fields
709 // rather than getting set as the field names with undefined values.
710 newData[name] = data[i];
712 else if (data[name] === undefined) {
713 data[name] = field.defaultValue;
717 me.set(newData || data);
721 } else if (me.phantom) {
722 newId = me.idgen.generate();
723 if (newId !== null) {
728 // clear any dirty/modified since we're initializing
732 if (typeof me.init == 'function') {
736 me.id = me.idgen.getRecId(me);
739 <span id='Ext-data-Model-method-get'> /**
740 </span> * Returns the value of the given field
741 * @param {String} fieldName The field to fetch the value for
742 * @return {Object} The value
744 get: function(field) {
745 return this[this.persistenceProperty][field];
748 <span id='Ext-data-Model-method-set'> /**
749 </span> * Sets the given field to the given value, marks the instance as dirty
750 * @param {String/Object} fieldName The field to set, or an object containing key/value pairs
751 * @param {Object} value The value to set
753 set: function(fieldName, value) {
756 modified = me.modified,
758 field, key, i, currentValue, notEditing, count, length;
761 * If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
762 * set those last so that all other possible data is set before the convert function is called
764 if (arguments.length == 1 && Ext.isObject(fieldName)) {
765 notEditing = !me.editing;
767 for (key in fieldName) {
768 if (fieldName.hasOwnProperty(key)) {
770 //here we check for the custom convert function. Note that if a field doesn't have a convert function,
771 //we default it to its type's convert function, so we have to check that here. This feels rather dirty.
772 field = fields.get(key);
773 if (field && field.convert !== field.type.convert) {
774 convertFields.push(key);
778 if (!count && notEditing) {
782 me.set(key, fieldName[key]);
786 length = convertFields.length;
788 if (!count && notEditing) {
792 for (i = 0; i < length; i++) {
793 field = convertFields[i];
794 me.set(field, fieldName[field]);
798 if (notEditing && count) {
803 field = fields.get(fieldName);
805 if (field && field.convert) {
806 value = field.convert(value, me);
809 currentValue = me.get(fieldName);
810 me[me.persistenceProperty][fieldName] = value;
812 if (field && field.persist && !me.isEqual(currentValue, value)) {
813 if (me.isModified(fieldName)) {
814 if (me.isEqual(modified[fieldName], value)) {
815 // the original value in me.modified equals the new value, so the
816 // field is no longer modified
817 delete modified[fieldName];
818 // we might have removed the last modified field, so check to see if
819 // there are any modified fields remaining and correct me.dirty:
821 for (key in modified) {
822 if (modified.hasOwnProperty(key)){
830 modified[fieldName] = currentValue;
840 <span id='Ext-data-Model-method-isEqual'> /**
841 </span> * Checks if two values are equal, taking into account certain
842 * special factors, for example dates.
844 * @param {Object} a The first value
845 * @param {Object} b The second value
846 * @return {Boolean} True if the values are equal
848 isEqual: function(a, b){
849 if (Ext.isDate(a) && Ext.isDate(b)) {
850 return a.getTime() === b.getTime();
855 <span id='Ext-data-Model-method-beginEdit'> /**
856 </span> * Begins an edit. While in edit mode, no events (e.g.. the `update` event) are relayed to the containing store.
857 * When an edit has begun, it must be followed by either {@link #endEdit} or {@link #cancelEdit}.
859 beginEdit : function(){
863 me.dirtySave = me.dirty;
864 me.dataSave = Ext.apply({}, me[me.persistenceProperty]);
865 me.modifiedSave = Ext.apply({}, me.modified);
869 <span id='Ext-data-Model-method-cancelEdit'> /**
870 </span> * Cancels all changes made in the current edit operation.
872 cancelEdit : function(){
876 // reset the modified state, nothing changed since the edit began
877 me.modified = me.modifiedSave;
878 me[me.persistenceProperty] = me.dataSave;
879 me.dirty = me.dirtySave;
880 delete me.modifiedSave;
886 <span id='Ext-data-Model-method-endEdit'> /**
887 </span> * Ends an edit. If any data was modified, the containing store is notified (ie, the store's `update` event will
889 * @param {Boolean} silent True to not notify the store of the change
891 endEdit : function(silent){
897 didChange = me.dirty || me.changedWhileEditing();
898 delete me.modifiedSave;
901 if (silent !== true && didChange) {
907 <span id='Ext-data-Model-method-changedWhileEditing'> /**
908 </span> * Checks if the underlying data has changed during an edit. This doesn't necessarily
909 * mean the record is dirty, however we still need to notify the store since it may need
910 * to update any views.
912 * @return {Boolean} True if the underlying data has changed during an edit.
914 changedWhileEditing: function(){
917 data = me[me.persistenceProperty],
921 if (data.hasOwnProperty(key)) {
922 if (!me.isEqual(data[key], saved[key])) {
930 <span id='Ext-data-Model-method-getChanges'> /**
931 </span> * Gets a hash of only the fields that have been modified since this Model was created or commited.
934 getChanges : function(){
935 var modified = this.modified,
939 for (field in modified) {
940 if (modified.hasOwnProperty(field)){
941 changes[field] = this.get(field);
948 <span id='Ext-data-Model-method-isModified'> /**
949 </span> * Returns true if the passed field name has been `{@link #modified}` since the load or last commit.
950 * @param {String} fieldName {@link Ext.data.Field#name}
953 isModified : function(fieldName) {
954 return this.modified.hasOwnProperty(fieldName);
957 <span id='Ext-data-Model-method-setDirty'> /**
958 </span> * Marks this **Record** as `{@link #dirty}`. This method is used interally when adding `{@link #phantom}` records
959 * to a {@link Ext.data.proxy.Server#writer writer enabled store}.
961 * Marking a record `{@link #dirty}` causes the phantom to be returned by {@link Ext.data.Store#getUpdatedRecords}
962 * where it will have a create action composed for it during {@link Ext.data.Model#save model save} operations.
964 setDirty : function() {
970 me.fields.each(function(field) {
973 me.modified[name] = me.get(name);
979 markDirty : function() {
980 if (Ext.isDefined(Ext.global.console)) {
981 Ext.global.console.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
983 return this.setDirty.apply(this, arguments);
987 <span id='Ext-data-Model-method-reject'> /**
988 </span> * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}. Rejects
989 * all changes made to the model instance since either creation, or the last commit operation. Modified fields are
990 * reverted to their original values.
992 * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of reject
995 * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
998 reject : function(silent) {
1000 modified = me.modified,
1003 for (field in modified) {
1004 if (modified.hasOwnProperty(field)) {
1005 if (typeof modified[field] != "function") {
1006 me[me.persistenceProperty][field] = modified[field];
1015 if (silent !== true) {
1020 <span id='Ext-data-Model-method-commit'> /**
1021 </span> * Usually called by the {@link Ext.data.Store} which owns the model instance. Commits all changes made to the
1022 * instance since either creation or the last commit operation.
1024 * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of commit
1027 * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
1028 * Defaults to false.
1030 commit : function(silent) {
1033 me.phantom = me.dirty = me.editing = false;
1036 if (silent !== true) {
1041 <span id='Ext-data-Model-method-copy'> /**
1042 </span> * Creates a copy (clone) of this Model instance.
1044 * @param {String} [id] A new id, defaults to the id of the instance being copied.
1045 * See `{@link Ext.data.Model#id id}`. To generate a phantom instance with a new id use:
1047 * var rec = record.copy(); // clone the record
1048 * Ext.data.Model.id(rec); // automatically generate a unique sequential id
1050 * @return {Ext.data.Model}
1052 copy : function(newId) {
1055 return new me.self(Ext.apply({}, me[me.persistenceProperty]), newId || me.internalId);
1058 <span id='Ext-data-Model-method-setProxy'> /**
1059 </span> * Sets the Proxy to use for this model. Accepts any options that can be accepted by
1060 * {@link Ext#createByAlias Ext.createByAlias}.
1062 * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
1063 * @return {Ext.data.proxy.Proxy}
1065 setProxy: function(proxy) {
1066 //make sure we have an Ext.data.proxy.Proxy object
1067 if (!proxy.isProxy) {
1068 if (typeof proxy === "string") {
1073 proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
1075 proxy.setModel(this.self);
1081 <span id='Ext-data-Model-method-getProxy'> /**
1082 </span> * Returns the configured Proxy for this Model.
1083 * @return {Ext.data.proxy.Proxy} The proxy
1085 getProxy: function() {
1089 <span id='Ext-data-Model-method-validate'> /**
1090 </span> * Validates the current data against all of its configured {@link #validations}.
1091 * @return {Ext.data.Errors} The errors object
1093 validate: function() {
1094 var errors = Ext.create('Ext.data.Errors'),
1095 validations = this.validations,
1096 validators = Ext.data.validations,
1097 length, validation, field, valid, type, i;
1100 length = validations.length;
1102 for (i = 0; i < length; i++) {
1103 validation = validations[i];
1104 field = validation.field || validation.name;
1105 type = validation.type;
1106 valid = validators[type](validation, this.get(field));
1111 message: validation.message || validators[type + 'Message']
1120 <span id='Ext-data-Model-method-isValid'> /**
1121 </span> * Checks if the model is valid. See {@link #validate}.
1122 * @return {Boolean} True if the model is valid.
1124 isValid: function(){
1125 return this.validate().isValid();
1128 <span id='Ext-data-Model-method-save'> /**
1129 </span> * Saves the model instance using the configured proxy.
1130 * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
1131 * @return {Ext.data.Model} The Model instance
1133 save: function(options) {
1134 options = Ext.apply({}, options);
1137 action = me.phantom ? 'create' : 'update',
1139 scope = options.scope || me,
1143 Ext.apply(options, {
1148 operation = Ext.create('Ext.data.Operation', options);
1150 callback = function(operation) {
1151 if (operation.wasSuccessful()) {
1152 record = operation.getRecords()[0];
1153 //we need to make sure we've set the updated data here. Ideally this will be redundant once the
1154 //ModelCache is in place
1155 me.set(record.data);
1156 record.dirty = false;
1158 Ext.callback(options.success, scope, [record, operation]);
1160 Ext.callback(options.failure, scope, [record, operation]);
1163 Ext.callback(options.callback, scope, [record, operation]);
1166 me.getProxy()[action](operation, callback, me);
1171 <span id='Ext-data-Model-method-destroy'> /**
1172 </span> * Destroys the model using the configured proxy.
1173 * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
1174 * @return {Ext.data.Model} The Model instance
1176 destroy: function(options){
1177 options = Ext.apply({}, options);
1181 scope = options.scope || me,
1185 Ext.apply(options, {
1190 operation = Ext.create('Ext.data.Operation', options);
1191 callback = function(operation) {
1192 if (operation.wasSuccessful()) {
1193 Ext.callback(options.success, scope, [record, operation]);
1195 Ext.callback(options.failure, scope, [record, operation]);
1197 Ext.callback(options.callback, scope, [record, operation]);
1200 me.getProxy().destroy(operation, callback, me);
1204 <span id='Ext-data-Model-method-getId'> /**
1205 </span> * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}.
1206 * @return {Number} The id
1209 return this.get(this.idProperty);
1212 <span id='Ext-data-Model-method-setId'> /**
1213 </span> * Sets the model instance's id field to the given id.
1214 * @param {Number} id The new id
1216 setId: function(id) {
1217 this.set(this.idProperty, id);
1220 <span id='Ext-data-Model-method-join'> /**
1221 </span> * Tells this model instance that it has been added to a store.
1222 * @param {Ext.data.Store} store The store to which this model has been added.
1224 join : function(store) {
1225 <span id='Ext-data-Model-property-store'> /**
1226 </span> * @property {Ext.data.Store} store
1227 * The {@link Ext.data.Store Store} to which this Record belongs.
1232 <span id='Ext-data-Model-method-unjoin'> /**
1233 </span> * Tells this model instance that it has been removed from the store.
1234 * @param {Ext.data.Store} store The store from which this model has been removed.
1236 unjoin: function(store) {
1240 <span id='Ext-data-Model-method-afterEdit'> /**
1242 * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
1243 * afterEdit method is called
1245 afterEdit : function() {
1246 this.callStore('afterEdit');
1249 <span id='Ext-data-Model-method-afterReject'> /**
1251 * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
1252 * afterReject method is called
1254 afterReject : function() {
1255 this.callStore("afterReject");
1258 <span id='Ext-data-Model-method-afterCommit'> /**
1260 * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
1261 * afterCommit method is called
1263 afterCommit: function() {
1264 this.callStore('afterCommit');
1267 <span id='Ext-data-Model-method-callStore'> /**
1269 * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
1270 * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
1271 * will always be called with the model instance as its single argument.
1272 * @param {String} fn The function to call on the store
1274 callStore: function(fn) {
1275 var store = this.store;
1277 if (store !== undefined && typeof store[fn] == "function") {
1282 <span id='Ext-data-Model-method-getAssociatedData'> /**
1283 </span> * Gets all of the data from this Models *loaded* associations. It does this recursively - for example if we have a
1284 * User which hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
1290 * status: 'shipped',
1298 * @return {Object} The nested data set for the Model's loaded associations
1300 getAssociatedData: function(){
1301 return this.prepareAssociatedData(this, [], null);
1304 <span id='Ext-data-Model-method-prepareAssociatedData'> /**
1306 * This complex-looking method takes a given Model instance and returns an object containing all data from
1307 * all of that Model's *loaded* associations. See (@link #getAssociatedData}
1308 * @param {Ext.data.Model} record The Model instance
1309 * @param {String[]} ids PRIVATE. The set of Model instance internalIds that have already been loaded
1310 * @param {String} associationType (optional) The name of the type of association to limit to.
1311 * @return {Object} The nested data set for the Model's loaded associations
1313 prepareAssociatedData: function(record, ids, associationType) {
1314 //we keep track of all of the internalIds of the models that we have loaded so far in here
1315 var associations = record.associations.items,
1316 associationCount = associations.length,
1317 associationData = {},
1318 associatedStore, associatedName, associatedRecords, associatedRecord,
1319 associatedRecordCount, association, id, i, j, type, allow;
1321 for (i = 0; i < associationCount; i++) {
1322 association = associations[i];
1323 type = association.type;
1325 if (associationType) {
1326 allow = type == associationType;
1328 if (allow && type == 'hasMany') {
1330 //this is the hasMany store filled with the associated data
1331 associatedStore = record[association.storeName];
1333 //we will use this to contain each associated record's data
1334 associationData[association.name] = [];
1336 //if it's loaded, put it into the association data
1337 if (associatedStore && associatedStore.data.length > 0) {
1338 associatedRecords = associatedStore.data.items;
1339 associatedRecordCount = associatedRecords.length;
1341 //now we're finally iterating over the records in the association. We do this recursively
1342 for (j = 0; j < associatedRecordCount; j++) {
1343 associatedRecord = associatedRecords[j];
1344 // Use the id, since it is prefixed with the model name, guaranteed to be unique
1345 id = associatedRecord.id;
1347 //when we load the associations for a specific model instance we add it to the set of loaded ids so that
1348 //we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
1349 if (Ext.Array.indexOf(ids, id) == -1) {
1352 associationData[association.name][j] = associatedRecord.data;
1353 Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids, type));
1357 } else if (allow && type == 'belongsTo') {
1358 associatedRecord = record[association.instanceName];
1359 if (associatedRecord !== undefined) {
1360 id = associatedRecord.id;
1361 if (Ext.Array.indexOf(ids, id) == -1) {
1363 associationData[association.name] = associatedRecord.data;
1364 Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids, type));
1370 return associationData;