4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../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
20 * @class Ext.data.Model
22 * <p>A Model represents some object that your application manages. For example, one might define a Model for Users, Products,
23 * 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},
24 * and are used by {@link Ext.data.Store stores}, which are in turn used by many of the data-bound components in Ext.</p>
26 * <p>Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:</p>
28 <pre><code>
30 extend: 'Ext.data.Model',
32 {name: 'name', type: 'string'},
33 {name: 'age', type: 'int'},
34 {name: 'phone', type: 'string'},
35 {name: 'alive', type: 'boolean', defaultValue: true}
38 changeName: function() {
39 var oldName = this.get('name'),
40 newName = oldName + " The Barbarian";
42 this.set('name', newName);
45 </code></pre>
47 * <p>The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link Ext.ModelManager ModelManager}, and all
48 * other functions and properties are copied to the new Model's prototype.</p>
50 * <p>Now we can create instances of our User model and call any model logic we defined:</p>
52 <pre><code>
53 var user = Ext.ModelManager.create({
60 user.get('name'); //returns "Conan The Barbarian"
61 </code></pre>
63 * <p><u>Validations</u></p>
65 * <p>Models have built-in support for validations, which are executed against the validator functions in
66 * {@link Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to models:</p>
68 <pre><code>
70 extend: 'Ext.data.Model',
72 {name: 'name', type: 'string'},
73 {name: 'age', type: 'int'},
74 {name: 'phone', type: 'string'},
75 {name: 'gender', type: 'string'},
76 {name: 'username', type: 'string'},
77 {name: 'alive', type: 'boolean', defaultValue: true}
81 {type: 'presence', field: 'age'},
82 {type: 'length', field: 'name', min: 2},
83 {type: 'inclusion', field: 'gender', list: ['Male', 'Female']},
84 {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
85 {type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
88 </code></pre>
90 * <p>The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
93 <pre><code>
94 var instance = Ext.ModelManager.create({
100 var errors = instance.validate();
101 </code></pre>
103 * <p><u>Associations</u></p>
105 * <p>Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and
106 * {@link Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
107 * application which deals with Users, Posts and Comments. We can express the relationships between these models like this:</p>
109 <pre><code>
111 extend: 'Ext.data.Model',
112 fields: ['id', 'user_id'],
115 hasMany : {model: 'Comment', name: 'comments'}
118 Ext.define('Comment', {
119 extend: 'Ext.data.Model',
120 fields: ['id', 'user_id', 'post_id'],
126 extend: 'Ext.data.Model',
131 {model: 'Comment', name: 'comments'}
134 </code></pre>
136 * <p>See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the usage
137 * and configuration of associations. Note that associations can also be specified like this:</p>
139 <pre><code>
141 extend: 'Ext.data.Model',
145 {type: 'hasMany', model: 'Post', name: 'posts'},
146 {type: 'hasMany', model: 'Comment', name: 'comments'}
149 </code></pre>
151 * <p><u>Using a Proxy</u></p>
153 * <p>Models are great for representing types of data and relationships, but sooner or later we're going to want to
154 * load or save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy},
155 * which can be set directly on the Model:</p>
157 <pre><code>
159 extend: 'Ext.data.Model',
160 fields: ['id', 'name', 'email'],
167 </code></pre>
169 * <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
170 * RESTful backend. Let's see how this works:</p>
172 <pre><code>
173 var user = Ext.ModelManager.create({name: 'Ed Spencer', email: 'ed@sencha.com'}, 'User');
175 user.save(); //POST /users
176 </code></pre>
178 * <p>Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this
179 * Model's data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't
180 * have an id, and performs the appropriate action - in this case issuing a POST request to the url we configured
181 * (/users). We configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full
184 * <p>Loading data via the Proxy is equally easy:</p>
186 <pre><code>
187 //get a reference to the User model class
188 var User = Ext.ModelManager.getModel('User');
190 //Uses the configured RestProxy to make a GET request to /users/123
192 success: function(user) {
193 console.log(user.getId()); //logs 123
196 </code></pre>
198 * <p>Models can also be updated and destroyed easily:</p>
200 <pre><code>
201 //the user Model we loaded in the last snippet:
202 user.set('name', 'Edward Spencer');
204 //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
206 success: function() {
207 console.log('The User was updated');
211 //tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
213 success: function() {
214 console.log('The User was destroyed!');
217 </code></pre>
219 * <p><u>Usage in Stores</u></p>
221 * <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
222 * by creating a {@link Ext.data.Store Store}:</p>
224 <pre><code>
225 var store = new Ext.data.Store({
229 //uses the Proxy we set up on Model to load the Store data
231 </code></pre>
233 * <p>A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain
234 * a set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the
235 * {@link Ext.data.Store Store docs} for more information on Stores.</p>
238 * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
239 * @param {Number} id (optional) Unique ID to assign to this model instance
241 Ext.define('Ext.data.Model', {
242 alternateClassName: 'Ext.data.Record',
245 observable: 'Ext.util.Observable'
252 'Ext.data.Operation',
253 'Ext.data.validations',
254 'Ext.data.proxy.Ajax',
255 'Ext.util.MixedCollection'
258 onClassExtended: function(cls, data) {
259 var onBeforeClassCreated = data.onBeforeClassCreated;
261 data.onBeforeClassCreated = function(cls, data) {
263 name = Ext.getClassName(cls),
264 prototype = cls.prototype,
265 superCls = cls.prototype.superclass,
267 validations = data.validations || [],
268 fields = data.fields || [],
269 associations = data.associations || [],
270 belongsTo = data.belongsTo,
271 hasMany = data.hasMany,
273 fieldsMixedCollection = new Ext.util.MixedCollection(false, function(field) {
277 associationsMixedCollection = new Ext.util.MixedCollection(false, function(association) {
278 return association.name;
281 superValidations = superCls.validations,
282 superFields = superCls.fields,
283 superAssociations = superCls.associations,
288 // Save modelName on class and its prototype
289 cls.modelName = name;
290 prototype.modelName = name;
292 // Merge the validations of the superclass and the new subclass
293 if (superValidations) {
294 validations = superValidations.concat(validations);
297 data.validations = validations;
299 // Merge the fields of the superclass and the new subclass
301 fields = superFields.items.concat(fields);
304 for (i = 0, ln = fields.length; i < ln; ++i) {
305 fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
308 data.fields = fieldsMixedCollection;
310 //associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
311 //we support that here
313 belongsTo = Ext.Array.from(belongsTo);
315 for (i = 0, ln = belongsTo.length; i < ln; ++i) {
316 association = belongsTo[i];
318 if (!Ext.isObject(association)) {
319 association = {model: association};
322 association.type = 'belongsTo';
323 associations.push(association);
326 delete data.belongsTo;
330 hasMany = Ext.Array.from(hasMany);
331 for (i = 0, ln = hasMany.length; i < ln; ++i) {
332 association = hasMany[i];
334 if (!Ext.isObject(association)) {
335 association = {model: association};
338 association.type = 'hasMany';
339 associations.push(association);
345 if (superAssociations) {
346 associations = superAssociations.items.concat(associations);
349 for (i = 0, ln = associations.length; i < ln; ++i) {
350 dependencies.push('association.' + associations[i].type.toLowerCase());
354 if (typeof data.proxy === 'string') {
355 dependencies.push('proxy.' + data.proxy);
357 else if (typeof data.proxy.type === 'string') {
358 dependencies.push('proxy.' + data.proxy.type);
362 Ext.require(dependencies, function() {
363 Ext.ModelManager.registerType(name, cls);
365 for (i = 0, ln = associations.length; i < ln; ++i) {
366 association = associations[i];
368 Ext.apply(association, {
370 associatedModel: association.model
373 if (Ext.ModelManager.getModel(association.model) === undefined) {
374 Ext.ModelManager.registerDeferredAssociation(association);
376 associationsMixedCollection.add(Ext.data.Association.create(association));
380 data.associations = associationsMixedCollection;
382 onBeforeClassCreated.call(me, cls, data);
384 cls.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);
386 // Fire the onModelDefined template method on ModelManager
387 Ext.ModelManager.onModelDefined(cls);
392 inheritableStatics: {
393 <span id='Ext-data-Model-method-setProxy'> /**
394 </span> * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
395 * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
398 setProxy: function(proxy) {
399 //make sure we have an Ext.data.proxy.Proxy object
400 if (!proxy.isProxy) {
401 if (typeof proxy == "string") {
406 proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
408 proxy.setModel(this);
409 this.proxy = this.prototype.proxy = proxy;
414 <span id='Ext-data-Model-method-getProxy'> /**
415 </span> * Returns the configured Proxy for this Model
416 * @return {Ext.data.proxy.Proxy} The proxy
418 getProxy: function() {
422 <span id='Ext-data-Model-method-load'> /**
423 </span> * <b>Static</b>. Asynchronously loads a model instance by id. Sample usage:
424 <pre><code>
425 MyApp.User = Ext.define('User', {
426 extend: 'Ext.data.Model',
428 {name: 'id', type: 'int'},
429 {name: 'name', type: 'string'}
433 MyApp.User.load(10, {
435 failure: function(record, operation) {
436 //do something if the load failed
438 success: function(record, operation) {
439 //do something if the load succeeded
441 callback: function(record, operation) {
442 //do something whether the load succeeded or failed
445 </code></pre>
446 * @param {Number} id The id of the model to load
447 * @param {Object} config Optional config object containing success, failure and callback functions, plus optional scope
448 * @member Ext.data.Model
452 load: function(id, config) {
453 config = Ext.apply({}, config);
454 config = Ext.applyIf(config, {
459 var operation = Ext.create('Ext.data.Operation', config),
460 scope = config.scope || this,
464 callback = function(operation) {
465 if (operation.wasSuccessful()) {
466 record = operation.getRecords()[0];
467 Ext.callback(config.success, scope, [record, operation]);
469 Ext.callback(config.failure, scope, [record, operation]);
471 Ext.callback(config.callback, scope, [record, operation]);
474 this.proxy.read(operation, callback, this);
479 PREFIX : 'ext-record',
485 <span id='Ext-data-Model-method-id'> /**
486 </span> * Generates a sequential id. This method is typically called when a record is {@link #create}d
487 * and {@link #Record no id has been specified}. The id will automatically be assigned
488 * to the record. The returned id takes the form:
489 * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
490 * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.PREFIX</tt>
491 * (defaults to <tt>'ext-record'</tt>)</p></li>
492 * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.AUTO_ID</tt>
493 * (defaults to <tt>1</tt> initially)</p></li>
494 * </ul></div>
495 * @param {Ext.data.Model} rec The record being created. The record does not exist, it's a {@link #phantom}.
496 * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
500 var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
507 <span id='Ext-data-Model-property-editing'> /**
508 </span> * Internal flag used to track whether or not the model instance is currently being edited. Read-only
514 <span id='Ext-data-Model-property-dirty'> /**
515 </span> * Readonly flag - true if this Record has been modified.
520 <span id='Ext-data-Model-cfg-persistenceProperty'> /**
521 </span> * @cfg {String} persistenceProperty The property on this Persistable object that its data is saved to.
522 * Defaults to 'data' (e.g. all persistable data resides in this.data.)
524 persistenceProperty: 'data',
529 <span id='Ext-data-Model-property-phantom'> /**
530 </span> * <tt>true</tt> when the record does not yet exist in a server-side database (see
531 * {@link #setDirty}). Any record which has a real database pk set as its id property
532 * is NOT a phantom -- it's real.
538 <span id='Ext-data-Model-cfg-idProperty'> /**
539 </span> * @cfg {String} idProperty The name of the field treated as this Model's unique id (defaults to 'id').
543 <span id='Ext-data-Model-property-defaultProxyType'> /**
544 </span> * The string type of the default Model Proxy. Defaults to 'ajax'
545 * @property defaultProxyType
548 defaultProxyType: 'ajax',
550 <span id='Ext-data-Model-property-fields'> /**
551 </span> * An array of the fields defined on this model
556 // raw not documented intentionally, meant to be used internally.
557 constructor: function(data, id, raw) {
566 isArray = Ext.isArray(data),
567 newData = isArray ? {} : null; // to hold mapped array data if needed
569 <span id='Ext-data-Model-property-internalId'> /**
570 </span> * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
571 * @property internalId
575 me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);
577 <span id='Ext-data-Model-property-raw'> /**
578 </span> * The raw data used to create this model if created via a reader.
588 <span id='Ext-data-Model-property-modified'> /**
589 </span> * Key: value pairs of all fields whose values have changed
595 // Deal with spelling error in previous releases
596 if (me.persistanceProperty) {
598 if (Ext.isDefined(Ext.global.console)) {
599 Ext.global.console.warn('Ext.data.Model: persistanceProperty has been deprecated. Use persistenceProperty instead.');
602 me.persistenceProperty = me.persistanceProperty;
604 me[me.persistenceProperty] = {};
606 me.mixins.observable.constructor.call(me);
608 //add default field values if present
609 fields = me.fields.items;
610 length = fields.length;
612 for (i = 0; i < length; i++) {
617 // Have to map array data so the values get assigned to the named fields
618 // rather than getting set as the field names with undefined values.
619 newData[name] = data[i];
621 else if (data[name] === undefined) {
622 data[name] = field.defaultValue;
626 me.set(newData || data);
627 // clear any dirty/modified since we're initializing
635 if (typeof me.init == 'function') {
639 me.id = me.modelName + '-' + me.internalId;
642 <span id='Ext-data-Model-method-get'> /**
643 </span> * Returns the value of the given field
644 * @param {String} fieldName The field to fetch the value for
645 * @return {Mixed} The value
647 get: function(field) {
648 return this[this.persistenceProperty][field];
651 <span id='Ext-data-Model-method-set'> /**
652 </span> * Sets the given field to the given value, marks the instance as dirty
653 * @param {String|Object} fieldName The field to set, or an object containing key/value pairs
654 * @param {Mixed} value The value to set
656 set: function(fieldName, value) {
659 modified = me.modified,
661 field, key, i, currentValue;
664 * If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
665 * set those last so that all other possible data is set before the convert function is called
667 if (arguments.length == 1 && Ext.isObject(fieldName)) {
668 for (key in fieldName) {
669 if (fieldName.hasOwnProperty(key)) {
671 //here we check for the custom convert function. Note that if a field doesn't have a convert function,
672 //we default it to its type's convert function, so we have to check that here. This feels rather dirty.
673 field = fields.get(key);
674 if (field && field.convert !== field.type.convert) {
675 convertFields.push(key);
679 me.set(key, fieldName[key]);
683 for (i = 0; i < convertFields.length; i++) {
684 field = convertFields[i];
685 me.set(field, fieldName[field]);
690 field = fields.get(fieldName);
692 if (field && field.convert) {
693 value = field.convert(value, me);
696 currentValue = me.get(fieldName);
697 me[me.persistenceProperty][fieldName] = value;
699 if (field && field.persist && !me.isEqual(currentValue, value)) {
700 if (me.isModified(fieldName)) {
701 if (me.isEqual(modified[fieldName], value)) {
702 // the original value in me.modified equals the new value, so the
703 // field is no longer modified
704 delete modified[fieldName];
705 // we might have removed the last modified field, so check to see if
706 // there are any modified fields remaining and correct me.dirty:
708 for (key in modified) {
709 if (modified.hasOwnProperty(key)){
717 modified[fieldName] = currentValue;
727 <span id='Ext-data-Model-method-isEqual'> /**
728 </span> * Checks if two values are equal, taking into account certain
729 * special factors, for example dates.
731 * @param {Object} a The first value
732 * @param {Object} b The second value
733 * @return {Boolean} True if the values are equal
735 isEqual: function(a, b){
736 if (Ext.isDate(a) && Ext.isDate(b)) {
737 return a.getTime() === b.getTime();
742 <span id='Ext-data-Model-method-beginEdit'> /**
743 </span> * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
744 * are relayed to the containing store. When an edit has begun, it must be followed
745 * by either {@link #endEdit} or {@link #cancelEdit}.
747 beginEdit : function(){
751 me.dirtySave = me.dirty;
752 me.dataSave = Ext.apply({}, me[me.persistenceProperty]);
753 me.modifiedSave = Ext.apply({}, me.modified);
757 <span id='Ext-data-Model-method-cancelEdit'> /**
758 </span> * Cancels all changes made in the current edit operation.
760 cancelEdit : function(){
764 // reset the modified state, nothing changed since the edit began
765 me.modified = me.modifiedSave;
766 me[me.persistenceProperty] = me.dataSave;
767 me.dirty = me.dirtySave;
768 delete me.modifiedSave;
774 <span id='Ext-data-Model-method-endEdit'> /**
775 </span> * End an edit. If any data was modified, the containing store is notified
776 * (ie, the store's <code>update</code> event will fire).
777 * @param {Boolean} silent True to not notify the store of the change
779 endEdit : function(silent){
783 delete me.modifiedSave;
786 if (silent !== true && me.dirty) {
792 <span id='Ext-data-Model-method-getChanges'> /**
793 </span> * Gets a hash of only the fields that have been modified since this Model was created or commited.
796 getChanges : function(){
797 var modified = this.modified,
801 for (field in modified) {
802 if (modified.hasOwnProperty(field)){
803 changes[field] = this.get(field);
810 <span id='Ext-data-Model-method-isModified'> /**
811 </span> * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
812 * since the load or last commit.
813 * @param {String} fieldName {@link Ext.data.Field#name}
816 isModified : function(fieldName) {
817 return this.modified.hasOwnProperty(fieldName);
820 <span id='Ext-data-Model-method-setDirty'> /**
821 </span> * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>. This method
822 * is used interally when adding <code>{@link #phantom}</code> records to a
823 * {@link Ext.data.Store#writer writer enabled store}.</p>
824 * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
825 * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
826 * have a create action composed for it during {@link Ext.data.Store#save store save}
827 * operations.</p>
829 setDirty : function() {
835 me.fields.each(function(field) {
838 me.modified[name] = me.get(name);
844 markDirty : function() {
845 if (Ext.isDefined(Ext.global.console)) {
846 Ext.global.console.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
848 return this.setDirty.apply(this, arguments);
852 <span id='Ext-data-Model-method-reject'> /**
853 </span> * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}.
854 * Rejects all changes made to the model instance since either creation, or the last commit operation.
855 * Modified fields are reverted to their original values.
856 * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
857 * to have their code notified of reject operations.</p>
858 * @param {Boolean} silent (optional) True to skip notification of the owning
859 * store of the change (defaults to false)
861 reject : function(silent) {
863 modified = me.modified,
866 for (field in modified) {
867 if (modified.hasOwnProperty(field)) {
868 if (typeof modified[field] != "function") {
869 me[me.persistenceProperty][field] = modified[field];
878 if (silent !== true) {
883 <span id='Ext-data-Model-method-commit'> /**
884 </span> * Usually called by the {@link Ext.data.Store} which owns the model instance.
885 * Commits all changes made to the instance since either creation or the last commit operation.
886 * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
887 * to have their code notified of commit operations.</p>
888 * @param {Boolean} silent (optional) True to skip notification of the owning
889 * store of the change (defaults to false)
891 commit : function(silent) {
899 if (silent !== true) {
904 <span id='Ext-data-Model-method-copy'> /**
905 </span> * Creates a copy (clone) of this Model instance.
906 * @param {String} id (optional) A new id, defaults to the id
907 * of the instance being copied. See <code>{@link #id}</code>.
908 * To generate a phantom instance with a new id use:<pre><code>
909 var rec = record.copy(); // clone the record
910 Ext.data.Model.id(rec); // automatically generate a unique sequential id
911 * </code></pre>
914 copy : function(newId) {
917 return new me.self(Ext.apply({}, me[me.persistenceProperty]), newId || me.internalId);
920 <span id='Ext-data-Model-method-setProxy'> /**
921 </span> * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
922 * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
925 setProxy: function(proxy) {
926 //make sure we have an Ext.data.proxy.Proxy object
927 if (!proxy.isProxy) {
928 if (typeof proxy === "string") {
933 proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
935 proxy.setModel(this.self);
941 <span id='Ext-data-Model-method-getProxy'> /**
942 </span> * Returns the configured Proxy for this Model
943 * @return {Ext.data.proxy.Proxy} The proxy
945 getProxy: function() {
949 <span id='Ext-data-Model-method-validate'> /**
950 </span> * Validates the current data against all of its configured {@link #validations} and returns an
951 * {@link Ext.data.Errors Errors} object
952 * @return {Ext.data.Errors} The errors object
954 validate: function() {
955 var errors = Ext.create('Ext.data.Errors'),
956 validations = this.validations,
957 validators = Ext.data.validations,
958 length, validation, field, valid, type, i;
961 length = validations.length;
963 for (i = 0; i < length; i++) {
964 validation = validations[i];
965 field = validation.field || validation.name;
966 type = validation.type;
967 valid = validators[type](validation, this.get(field));
972 message: validation.message || validators[type + 'Message']
981 <span id='Ext-data-Model-method-isValid'> /**
982 </span> * Checks if the model is valid. See {@link #validate}.
983 * @return {Boolean} True if the model is valid.
986 return this.validate().isValid();
989 <span id='Ext-data-Model-method-save'> /**
990 </span> * Saves the model instance using the configured proxy
991 * @param {Object} options Options to pass to the proxy
992 * @return {Ext.data.Model} The Model instance
994 save: function(options) {
995 options = Ext.apply({}, options);
998 action = me.phantom ? 'create' : 'update',
1000 scope = options.scope || me,
1004 Ext.apply(options, {
1009 operation = Ext.create('Ext.data.Operation', options);
1011 callback = function(operation) {
1012 if (operation.wasSuccessful()) {
1013 record = operation.getRecords()[0];
1014 //we need to make sure we've set the updated data here. Ideally this will be redundant once the
1015 //ModelCache is in place
1016 me.set(record.data);
1017 record.dirty = false;
1019 Ext.callback(options.success, scope, [record, operation]);
1021 Ext.callback(options.failure, scope, [record, operation]);
1024 Ext.callback(options.callback, scope, [record, operation]);
1027 me.getProxy()[action](operation, callback, me);
1032 <span id='Ext-data-Model-method-destroy'> /**
1033 </span> * Destroys the model using the configured proxy
1034 * @param {Object} options Options to pass to the proxy
1035 * @return {Ext.data.Model} The Model instance
1037 destroy: function(options){
1038 options = Ext.apply({}, options);
1042 scope = options.scope || me,
1046 Ext.apply(options, {
1051 operation = Ext.create('Ext.data.Operation', options);
1052 callback = function(operation) {
1053 if (operation.wasSuccessful()) {
1054 Ext.callback(options.success, scope, [record, operation]);
1056 Ext.callback(options.failure, scope, [record, operation]);
1058 Ext.callback(options.callback, scope, [record, operation]);
1061 me.getProxy().destroy(operation, callback, me);
1065 <span id='Ext-data-Model-method-getId'> /**
1066 </span> * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}
1067 * @return {Number} The id
1070 return this.get(this.idProperty);
1073 <span id='Ext-data-Model-method-setId'> /**
1074 </span> * Sets the model instance's id field to the given id
1075 * @param {Number} id The new id
1077 setId: function(id) {
1078 this.set(this.idProperty, id);
1081 <span id='Ext-data-Model-method-join'> /**
1082 </span> * Tells this model instance that it has been added to a store
1083 * @param {Ext.data.Store} store The store that the model has been added to
1085 join : function(store) {
1086 <span id='Ext-data-Model-property-store'> /**
1087 </span> * The {@link Ext.data.Store} to which this Record belongs.
1089 * @type {Ext.data.Store}
1094 <span id='Ext-data-Model-method-unjoin'> /**
1095 </span> * Tells this model instance that it has been removed from the store
1097 unjoin: function() {
1101 <span id='Ext-data-Model-method-afterEdit'> /**
1103 * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
1104 * afterEdit method is called
1106 afterEdit : function() {
1107 this.callStore('afterEdit');
1110 <span id='Ext-data-Model-method-afterReject'> /**
1112 * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
1113 * afterReject method is called
1115 afterReject : function() {
1116 this.callStore("afterReject");
1119 <span id='Ext-data-Model-method-afterCommit'> /**
1121 * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
1122 * afterCommit method is called
1124 afterCommit: function() {
1125 this.callStore('afterCommit');
1128 <span id='Ext-data-Model-method-callStore'> /**
1130 * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
1131 * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
1132 * will always be called with the model instance as its single argument.
1133 * @param {String} fn The function to call on the store
1135 callStore: function(fn) {
1136 var store = this.store;
1138 if (store !== undefined && typeof store[fn] == "function") {
1143 <span id='Ext-data-Model-method-getAssociatedData'> /**
1144 </span> * Gets all of the data from this Models *loaded* associations.
1145 * It does this recursively - for example if we have a User which
1146 * hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
1151 * status: 'shipped',
1158 * @return {Object} The nested data set for the Model's loaded associations
1160 getAssociatedData: function(){
1161 return this.prepareAssociatedData(this, [], null);
1164 <span id='Ext-data-Model-method-prepareAssociatedData'> /**
1166 * This complex-looking method takes a given Model instance and returns an object containing all data from
1167 * all of that Model's *loaded* associations. See (@link #getAssociatedData}
1168 * @param {Ext.data.Model} record The Model instance
1169 * @param {Array} ids PRIVATE. The set of Model instance internalIds that have already been loaded
1170 * @param {String} associationType (optional) The name of the type of association to limit to.
1171 * @return {Object} The nested data set for the Model's loaded associations
1173 prepareAssociatedData: function(record, ids, associationType) {
1174 //we keep track of all of the internalIds of the models that we have loaded so far in here
1175 var associations = record.associations.items,
1176 associationCount = associations.length,
1177 associationData = {},
1178 associatedStore, associatedName, associatedRecords, associatedRecord,
1179 associatedRecordCount, association, id, i, j, type, allow;
1181 for (i = 0; i < associationCount; i++) {
1182 association = associations[i];
1183 type = association.type;
1185 if (associationType) {
1186 allow = type == associationType;
1188 if (allow && type == 'hasMany') {
1190 //this is the hasMany store filled with the associated data
1191 associatedStore = record[association.storeName];
1193 //we will use this to contain each associated record's data
1194 associationData[association.name] = [];
1196 //if it's loaded, put it into the association data
1197 if (associatedStore && associatedStore.data.length > 0) {
1198 associatedRecords = associatedStore.data.items;
1199 associatedRecordCount = associatedRecords.length;
1201 //now we're finally iterating over the records in the association. We do this recursively
1202 for (j = 0; j < associatedRecordCount; j++) {
1203 associatedRecord = associatedRecords[j];
1204 // Use the id, since it is prefixed with the model name, guaranteed to be unique
1205 id = associatedRecord.id;
1207 //when we load the associations for a specific model instance we add it to the set of loaded ids so that
1208 //we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
1209 if (Ext.Array.indexOf(ids, id) == -1) {
1212 associationData[association.name][j] = associatedRecord.data;
1213 Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids, type));
1217 } else if (allow && type == 'belongsTo') {
1218 associatedRecord = record[association.instanceName];
1219 if (associatedRecord !== undefined) {
1220 id = associatedRecord.id;
1221 if (Ext.Array.indexOf(ids, id) == -1) {
1223 associationData[association.name] = associatedRecord.data;
1224 Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids, type));
1230 return associationData;