Created
July 26, 2011 13:37
-
-
Save esatterwhite/1106777 to your computer and use it in GitHub Desktop.
Class For rendering and validating forms in Sencha Touch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Module for General utility functions and classes | |
* @module utils | |
* | |
*/ | |
Ext.ns('BlackjackM','BlackjackM.utils', 'BlackjackM.utils.forms'); | |
/** | |
* A class which renders and validates a form based on a Ext.data.Model or Model instace | |
* @class ModelForm | |
* @namespace BlackjackM.utils.forms | |
*/ | |
BlackjackM.utils.forms.ModelForm = Ext.extend(Ext.form.FormPanel, { | |
/** | |
* the name of the model to be used to render and validate the form | |
* This is required if an instance isn't given | |
* @config model | |
* @type String | |
* @default null | |
*/ | |
model: null | |
/** | |
* <optional> a model instance to be used to rnder and validate the form against | |
* @config instance | |
* @type Ext.data.Model | |
* @default null | |
*/ | |
,instance: null | |
/** | |
* A set of key value pairs used in conjunction with the model property to pre populate the form with | |
* @config initial | |
* @type Object | |
* @default {} | |
*/ | |
,initial: {} | |
/** | |
* The css class to apply to invalid fields | |
* @config errorCls | |
* @type String | |
* @default x-field-error | |
*/ | |
,errorCls: 'x-field-error' | |
/** | |
* If set to true each field will be rendered in it's own fieldset | |
* @config fieldsets | |
* @type boolean | |
* @default false | |
*/ | |
,fieldsets: false | |
/** | |
* The name of a field or an a array of field names to exclude from form rendering | |
* @config excludes | |
* @type {String | Array} | |
* @default [] | |
*/ | |
,exclude:[] | |
/** | |
* names of fiels to be rendered as hidden input field | |
* @config hiddenFields | |
* @type {String | Array} | |
* @default [] | |
*/ | |
,hiddenFields:[] | |
,wrapForm: false | |
/** | |
* The Contstructor class | |
* @constructor | |
* @method initComponent | |
*/ | |
,initComponent: function () { | |
this.fieldcache = []; | |
if ( !this.model && !this.instance) { | |
throw "ModelForm requires a model type or a Model instance"; | |
} | |
this.addEvents({ | |
formvalid: true | |
,forminvalid: true | |
,submitsuccess: true}); | |
this.instance = this.instance || Ext.ModelMgr.create(this.initial || {}, this.model); | |
Ext.apply(this, { | |
items: this.fieldsets ? this.asFieldSets() : this.asFields() | |
}); | |
this.on('afterlayout', function( c ){ | |
Ext.each(this.query('field[rel=modelform-field]'), function (fld) { | |
fld.on('focus', function () { | |
fld.removeCls(this.errorCls); | |
}, this); | |
}, this); | |
}) | |
BlackjackM.utils.forms.ModelForm.superclass.initComponent.call(this); | |
} | |
/** | |
* Sets up the focus event listener to remove error css class from fields | |
* @private | |
* @method afterRender | |
*/ | |
,afterRender: function () { | |
this.loadRecord(this.instance); | |
// loadRecord saves the instance as `record` | |
delete this.instance; | |
BlackjackM.utils.forms.ModelForm.superclass.afterRender.call(this); | |
} | |
/** | |
* Validates the form againes it's internal model instance and returns an errors object and adds error class to invalid fields | |
* Fields that were excluded from the form do not got through validation | |
* @method validate | |
* @return Ext.data.Errors | |
*/ | |
,validate: function () { | |
var excludes | |
,errors | |
,filterFn | |
,addErrors; | |
this.updateRecord(this.record); | |
excludes = Array.from( this.exclude ); | |
errors = this.record.validate(); | |
filterFn = function(obj, key){ | |
return !excludes.contains( obj.field ); | |
}; | |
addErrors = function (err) { | |
var field = this.getFields(err.field); | |
field.addCls(this.errorCls); | |
}; | |
// don't validate fields that were excluded from the form. | |
// they will always fail | |
errors = errors.filterBy( filterFn ); | |
addErrors = Ext.createDelegate( addErrors, this ); | |
errors.each(addErrors, this); | |
if ( errors.isValid() ) { | |
/** | |
* Fired when the form passes validation | |
* @event formvalid | |
* @param form {ModelForm} The current form instance | |
*/ | |
this.fireEvent('formvalid', this); | |
} else { | |
/** | |
* Fired when the form fails validation | |
* @event forminvalid | |
* @param form {ModelForm} The current form instance | |
* @param err {Ext.data.Errors} The collection of errors returned from validation | |
*/ | |
this.fireEvent('forminvalid', this, errors); | |
} | |
this.errors = errors; | |
return errors; | |
} | |
/** | |
* Returns true if the form passes validation | |
* @method isValid | |
* @return boolean | |
*/ | |
,isValid: function () { | |
return !this.validate().length; | |
} | |
/** | |
* Clears error class from all form fields | |
* @method clearInvalid | |
* @return form {ModelForm} the form instance | |
*/ | |
,clearInvalid: function (){ | |
var clear_func = function( field ){ | |
this.getFields( field.name ).removeCls( this.errorCls ); | |
}; | |
clear_func = Ext.createDelegate( clear_func, this ); | |
this.record.fields.each( clear_func ); | |
return this; | |
} | |
/** | |
* maks a field as being invalid | |
* @method markInvalid | |
* @param name {String} The name of the field to mark invalid | |
* @return form {ModelForm} the form instance | |
*/ | |
,markInvalid: function ( name ) { | |
var fld = this.getFields( name ); | |
if( fld ){ | |
fld.addCls( this.errorCls ); | |
} | |
return this; | |
} | |
/** | |
* returns an array of form field config objects | |
* @method asFields | |
* @return Array | |
* @private | |
*/ | |
,asFields: function ( ) { | |
var excludes = Array.from( this.exclude ) | |
,hiddens = Array.from( this.hiddenFields ) | |
,that = this | |
,flds = [] | |
,generator; | |
generator = function (field) { | |
var overrideCfg; | |
overrideCfg = that[ field.name + 'Cfg'] || null; | |
if( !excludes.contains( field.name ) ){ | |
flds.push( Ext.apply({ | |
name: field.name | |
,label:field.name.replace(/_/g, ' ') | |
,rel:'modelform-field' | |
,xtype:hiddens.contains(field.name) ?'hiddenfield' : field.fieldtype || 'textfield' | |
}, overrideCfg || field.fieldCfg || {}) | |
); | |
} | |
}; | |
this.instance.fields.each( generator, this ); | |
return this.wrapForm ? new Ext.Container({ | |
cls:'modelform-wrap' | |
,items:flds.clean() | |
}) : flds.clean(); | |
} | |
/** | |
* Returns an array of form field config objects each wrapped in a fieldset config object | |
* @method asFieldSets | |
* @return Array | |
* @private | |
*/ | |
,asFieldSets: function ( ) { | |
var excludes = Array.from( this.exclude ) | |
,hiddens = Array.from( this.hiddenFields ) | |
,that = this | |
,flds =[] | |
,generator; | |
generator = function (field) { | |
var should_hide | |
,itemcfg | |
,overrideCfg; | |
should_hide = hiddens.contains(field.name); | |
overrideCfg = that[ field.name + 'Cfg'] || null; | |
if( !excludes.contains( field.name ) ){ | |
itemcfg = Ext.apply({ | |
name: field.name | |
,rel:'modelform-field' | |
,xtype: should_hide?'hiddenfield' : field.fieldtype || 'textfield' | |
}, overrideCfg || field.fieldCfg || {}); | |
flds.push( | |
should_hide ? itemcfg:{ | |
xtype: 'fieldset' | |
,title: itemcfg.name.replace(/_/g, ' ') | |
,items: [itemcfg] | |
} | |
); | |
} | |
}; | |
this.instance.fields.each( generator, this ); | |
return this.wrapForm ? new Ext.Container({ | |
cls:'modelform-wrap' | |
,items:flds.clean() | |
}) : flds.clean(); | |
} | |
/** | |
* Returns the current values out of the associated model instance | |
* @method getModelValues | |
* @return values {Object} an object containing data from the model where keys are names of the fields | |
* @protected | |
*/ | |
,getModelValues: function(){ | |
var excludes = Array.from( this.exclude ); | |
var ret_data = {}; | |
var model_data = this.record[ this.record.persistanceProperty ]; | |
for( var key in model_data ){ | |
if( !excludes.contains( key ) ){ | |
ret_data[key] = model_data[ key ]; | |
} | |
} | |
return ret_data; | |
} | |
,getCleanValues: function(){ | |
var data = this.getModelValues(); | |
return this.cleanFormData( data ); | |
} | |
/** | |
* A hook which allows developer to modify the data just before it is sent to the server | |
* By default the data just passes straight through | |
* @method cleanFormdata <override> | |
* @return form_data {object} the data object to send to the server | |
*/ | |
,cleanFormData: function( data ){ | |
return data; | |
} | |
/** | |
* | |
* @method submit | |
* @param config {Object} config object for the submit function | |
* @return form {ModelForm} the current form instance | |
*/ | |
,submit: function( config ){ | |
var errors | |
,url | |
,conn | |
,tmp_data | |
,model_data; | |
errors = this.validate(); | |
if( !!errors.length){ | |
return false; | |
} | |
config = config || {}; | |
url = config.url || this.url; | |
if( !url ){ throw "You must specify a url propery :: ModelForm.js";} | |
model_data = this.getModelValues(); | |
Object.merge( model_data, ( config.data || {})); | |
/** | |
* Fired just before the clean step takes place | |
* @event beforeclean | |
* @param data {Object} the data before it is cleaned | |
*/ | |
this.fireEvent('beforeclean', model_data, this.record); | |
// the function can alter the internal data, but can not change the | |
// object it self. if the developer returns a value, we will use that | |
// otherwise we continue on. | |
tmp_data = this.cleanFormData( model_data ); | |
if( tmp_data ){ | |
model_data = tmp_data; | |
tmp_data = null; | |
} | |
/** | |
* Fired just after the form data has been cleaned | |
* @event afterclean | |
* @param data {Object} the data after it has been cleaned | |
*/ | |
this.fireEvent('afterclean', model_data, this.record); | |
if( config.ajax && !this.fileUpload){ | |
conn = new BlackjackM.Ajax({ | |
url:url | |
,method: ( config.method || 'POST' ) | |
,scope:this | |
,autoSend:true | |
,headers:{ | |
'Accept':'application/json' | |
,'X-Requested-With':'XMLHttpRequest' | |
,'Content-Type':"application/json" | |
} | |
,jsonData: model_data | |
,success: function( response ){ | |
if( config.success && typeof config.success == 'function'){ | |
config.success.call( config.scope || this, response, this.record); | |
} | |
/** | |
* Fired when the form is successfully submitted | |
* @event submitsuccess | |
* @param form {ModelForm} the current form instance | |
* @param response {Object} the response object returned from the server | |
*/ | |
this.fireEvent('submitsuccess', this, response, this.record); | |
} | |
}); | |
}else{ | |
BlackjackM.utils.forms.ModelForm.superclass.submit.call( this, config ); | |
} | |
return this; | |
} | |
/** | |
* removes error classes and sets the fiels to their default values | |
* @method reset | |
* @return form {ModelForm} the current instance | |
*/ | |
,reset: function(){ | |
this.fields.each(function( field ){ | |
field.removeCls(this.errorCls) | |
}, this) | |
BlackjackM.utils.forms.ModelForm.superclass.reset.call( this ); | |
return this; | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment