/** * @author Ed Spencer * @class Ext.data.Model * @extends Ext.util.Stateful * *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.ModelMgr 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.
* *Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:
** *Ext.regModel('User', { 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); } });
The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link Ext.ModelMgr ModelMgr}, and all * other functions and properties are copied to the new Model's prototype.
* *Now we can create instances of our User model and call any model logic we defined:
** *var user = Ext.ModelMgr.create({ name : 'Conan', age : 24, phone: '555-555-5555' }, 'User'); user.changeName(); user.get('name'); //returns "Conan The Barbarian"
Validations
* *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:
** *Ext.regModel('User', { 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}/} ] });
The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors} * object:
** *var instance = Ext.ModelMgr.create({ name: 'Ed', gender: 'Male', username: 'edspencer' }, 'User'); var errors = instance.validate();
Associations
* *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:
** *Ext.regModel('Post', { fields: ['id', 'user_id'], belongsTo: 'User', hasMany : {model: 'Comment', name: 'comments'} }); Ext.regModel('Comment', { fields: ['id', 'user_id', 'post_id'], belongsTo: 'Post' }); Ext.regModel('User', { fields: ['id'], hasMany: [ 'Post', {model: 'Comment', name: 'comments'} ] });
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:
** * @constructor * @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 */ Ext.data.Model = Ext.extend(Ext.util.Stateful, { evented: false, isModel: true, /** * 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. * @property phantom * @type {Boolean} */ phantom : false, /** * The name of the field treated as this Model's unique id (defaults to 'id'). * @property idProperty * @type String */ idProperty: 'id', constructor: function(data, id) { data = data || {}; if (this.evented) { this.addEvents( ); } /** * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet * @property internalId * @type String * @private */ this.internalId = (id || id === 0) ? id : Ext.data.Model.id(this); Ext.data.Model.superclass.constructor.apply(this); //add default field values if present var fields = this.fields.items, length = fields.length, field, name, i; for (i = 0; i < length; i++) { field = fields[i]; name = field.name; if (data[name] == undefined) { data[name] = field.defaultValue; } } this.set(data); if (this.getId()) { this.phantom = false; } if (typeof this.init == 'function') { this.init(); } }, /** * Validates the current data against all of its configured {@link #validations} and returns an * {@link Ext.data.Errors Errors} object * @return {Ext.data.Errors} The errors object */ validate: function() { var errors = new Ext.data.Errors(), validations = this.validations, validators = Ext.data.validations, length, validation, field, valid, type, i; if (validations) { length = validations.length; for (i = 0; i < length; i++) { validation = validations[i]; field = validation.field || validation.name; type = validation.type; valid = validators[type](validation, this.get(field)); if (!valid) { errors.add({ field : field, message: validation.message || validators[type + 'Message'] }); } } } return errors; }, /** * Returns the configured Proxy for this Model * @return {Ext.data.Proxy} The proxy */ getProxy: function() { return this.constructor.proxy; }, /** * Saves the model instance using the configured proxy * @param {Object} options Options to pass to the proxy * @return {Ext.data.Model} The Model instance */ save: function(options) { var action = this.phantom ? 'create' : 'update'; options = options || {}; Ext.apply(options, { records: [this], action : action }); var operation = new Ext.data.Operation(options), successCb = options.success, failureCb = options.failure, callbackFn = options.callback, scope = options.scope, record; var callback = function(operation) { record = operation.getRecords()[0]; if (operation.wasSuccessful()) { record.dirty = false; if (typeof successCb == 'function') { successCb.call(scope, record, operation); } } else { if (typeof failureCb == 'function') { failureCb.call(scope, record, operation); } } if (typeof callbackFn == 'function') { callbackFn.call(scope, record, operation); } }; this.getProxy()[action](operation, callback, this); return this; }, /** * 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 * @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 */ join : function(store) { /** * The {@link Ext.data.Store} to which this Record belongs. * @property store * @type {Ext.data.Store} */ this.store = store; }, /** * Tells this model instance that it has been removed from the store * @param {Ext.data.Store} store The store to unjoin */ 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 */ afterEdit : function() { 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 */ afterReject : function() { 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 */ afterCommit: function() { 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 * will always be called with the model instance as its single argument. * @param {String} fn The function to call on the store */ callStore: function(fn) { var store = this.store; if (store != undefined && typeof store[fn] == "function") { store[fn](this); } } }); Ext.apply(Ext.data.Model, { /** * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext.data.ProxyMgr#create} * @param {String/Object/Ext.data.Proxy} proxy The proxy */ setProxy: function(proxy) { //make sure we have an Ext.data.Proxy object proxy = Ext.data.ProxyMgr.create(proxy); proxy.setModel(this); this.proxy = proxy; return proxy; }, /** * Asynchronously loads a model instance by id. Sample usage:Ext.regModel('User', { fields: ['id'], associations: [ {type: 'hasMany', model: 'Post', name: 'posts'}, {type: 'hasMany', model: 'Comment', name: 'comments'} ] });
* @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 */ load: function(id, config) { config = Ext.applyIf(config || {}, { action: 'read', id : id }); var operation = new Ext.data.Operation(config), callbackFn = config.callback, successCb = config.success, failureCb = config.failure, scope = config.scope, record, callback; callback = function(operation) { record = operation.getRecords()[0]; if (operation.wasSuccessful()) { if (typeof successCb == 'function') { successCb.call(scope, record, operation); } } else { if (typeof failureCb == 'function') { failureCb.call(scope, record, operation); } } if (typeof callbackFn == 'function') { callbackFn.call(scope, record, operation); } }; this.proxy.read(operation, callback, this); } }); /** * 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 returned id takes the form: * {PREFIX}-{AUTO_ID}.MyApp.User = Ext.regModel('User', { 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 {Record} rec The record being created. The record does not exist, it's a {@link #phantom}. * @return {String} auto-generated string id, "ext-record-i++'; */ Ext.data.Model.id = function(rec) { rec.phantom = true; return [Ext.data.Model.PREFIX, '-', Ext.data.Model.AUTO_ID++].join(''); }; //[deprecated 5.0] Ext.ns('Ext.data.Record'); //Backwards compat Ext.data.Record.id = Ext.data.Model.id; //[end] Ext.data.Model.PREFIX = 'ext-record'; Ext.data.Model.AUTO_ID = 1; Ext.data.Model.EDIT = 'edit'; Ext.data.Model.REJECT = 'reject'; Ext.data.Model.COMMIT = 'commit';*
- PREFIX : String
*Ext.data.Model.PREFIX * (defaults to 'ext-record')
- AUTO_ID : String
*Ext.data.Model.AUTO_ID * (defaults to 1 initially)