X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/ee06f37b0f6f6d94cd05a6ffae556660f7c4a2bc..c930e9176a5a85509c5b0230e2bff5c22a591432:/src/widgets/form/Form.js diff --git a/src/widgets/form/Form.js b/src/widgets/form/Form.js new file mode 100644 index 00000000..78359d0a --- /dev/null +++ b/src/widgets/form/Form.js @@ -0,0 +1,335 @@ +/*! + * Ext JS Library 3.0.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.form.FormPanel + * @extends Ext.Panel + *

Standard form container.

+ * + *

Layout

+ *

By default, FormPanel is configured with layout:'form' to use an {@link Ext.layout.FormLayout} + * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers + * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the + * {@link Ext.layout.FormLayout} layout manager.

+ * + *

BasicForm

+ *

Although not listed as configuration options of FormPanel, the FormPanel class accepts all + * of the config options required to configure its internal {@link Ext.form.BasicForm} for: + *

+ * + *

Note: If subclassing FormPanel, any configuration options for the BasicForm must be applied to + * the initialConfig property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm} + * configuration settings to this will not affect the BasicForm's configuration.

+ * + *

Form Validation

+ *

For information on form validation see the following:

+ *
+ * + *

Form Submission

+ *

By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser + * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the + * {@link Ext.form.BasicForm#standardSubmit standardSubmit} option.

+ * + * @constructor + * @param {Object} config Configuration options + * @xtype form + */ +Ext.FormPanel = Ext.extend(Ext.Panel, { + /** + * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id). + */ + /** + * @cfg {Boolean} hideLabels + *

true to hide field labels by default (sets display:none). Defaults to + * false.

+ *

Also see {@link Ext.Component}.{@link Ext.Component#hideLabel hideLabel}. + */ + /** + * @cfg {Number} labelPad + * The default padding in pixels for field labels (defaults to 5). labelPad only + * applies if {@link #labelWidth} is also specified, otherwise it will be ignored. + */ + /** + * @cfg {String} labelSeparator + * See {@link Ext.Component}.{@link Ext.Component#labelSeparator labelSeparator} + */ + /** + * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers + * and can be overridden on any child container (e.g., a fieldset can specify a different labelWidth + * for its fields) (defaults to 100). + */ + /** + * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers. + */ + /** + * @cfg {Array} buttons + * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.
+ *

Buttons in the footer of a FormPanel may be configured with the option formBind: true. This causes + * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on + * the form's valid/invalid state.

+ */ + + + /** + * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75). + */ + minButtonWidth : 75, + + /** + * @cfg {String} labelAlign The label alignment value used for the text-align specification + * for the container. Valid values are "left", "top" or "right" + * (defaults to "left"). This property cascades to child containers and can be + * overridden on any child container (e.g., a fieldset can specify a different labelAlign + * for its fields). + */ + labelAlign : 'left', + + /** + * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and + * regularly fires the {@link #clientvalidation} event passing that state.
+ *

When monitoring valid state, the FormPanel enables/disables any of its configured + * {@link #buttons} which have been configured with formBind: true depending + * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to false

+ */ + monitorValid : false, + + /** + * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200) + */ + monitorPoll : 200, + + /** + * @cfg {String} layout Defaults to 'form'. Normally this configuration property should not be altered. + * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}. + */ + layout : 'form', + + // private + initComponent : function(){ + this.form = this.createForm(); + Ext.FormPanel.superclass.initComponent.call(this); + + this.bodyCfg = { + tag: 'form', + cls: this.baseCls + '-body', + method : this.method || 'POST', + id : this.formId || Ext.id() + }; + if(this.fileUpload) { + this.bodyCfg.enctype = 'multipart/form-data'; + } + this.initItems(); + + this.addEvents( + /** + * @event clientvalidation + * If the monitorValid config option is true, this event fires repetitively to notify of valid state + * @param {Ext.form.FormPanel} this + * @param {Boolean} valid true if the form has passed client-side validation + */ + 'clientvalidation' + ); + + this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']); + }, + + // private + createForm : function(){ + var config = Ext.applyIf({listeners: {}}, this.initialConfig); + return new Ext.form.BasicForm(null, config); + }, + + // private + initFields : function(){ + var f = this.form; + var formPanel = this; + var fn = function(c){ + if(formPanel.isField(c)){ + f.add(c); + }if(c.isFieldWrap){ + Ext.applyIf(c, { + labelAlign: c.ownerCt.labelAlign, + labelWidth: c.ownerCt.labelWidth, + itemCls: c.ownerCt.itemCls + }); + f.add(c.field); + }else if(c.doLayout && c != formPanel){ + Ext.applyIf(c, { + labelAlign: c.ownerCt.labelAlign, + labelWidth: c.ownerCt.labelWidth, + itemCls: c.ownerCt.itemCls + }); + //each check required for check/radio groups. + if(c.items && c.items.each){ + c.items.each(fn, this); + } + } + }; + this.items.each(fn, this); + }, + + // private + getLayoutTarget : function(){ + return this.form.el; + }, + + /** + * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains. + * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains. + */ + getForm : function(){ + return this.form; + }, + + // private + onRender : function(ct, position){ + this.initFields(); + Ext.FormPanel.superclass.onRender.call(this, ct, position); + this.form.initEl(this.body); + }, + + // private + beforeDestroy : function(){ + this.stopMonitoring(); + Ext.FormPanel.superclass.beforeDestroy.call(this); + /* + * Clear the items here to prevent them being destroyed again. + * Don't move this behaviour to BasicForm because it can be used + * on it's own. + */ + this.form.items.clear(); + Ext.destroy(this.form); + }, + + // Determine if a Component is usable as a form Field. + isField : function(c) { + return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid; + }, + + // private + initEvents : function(){ + Ext.FormPanel.superclass.initEvents.call(this); + this.on('remove', this.onRemove, this); + this.on('add', this.onAdd, this); + if(this.monitorValid){ // initialize after render + this.startMonitoring(); + } + }, + + // private + onAdd : function(ct, c) { + // If a single form Field, add it + if (this.isField(c)) { + this.form.add(c); + // If a Container, add any Fields it might contain + } else if (c.findBy) { + Ext.applyIf(c, { + labelAlign: c.ownerCt.labelAlign, + labelWidth: c.ownerCt.labelWidth, + itemCls: c.ownerCt.itemCls + }); + this.form.add.apply(this.form, c.findBy(this.isField)); + } + }, + + // private + onRemove : function(ct, c) { + // If a single form Field, remove it + if (this.isField(c)) { + Ext.destroy(c.container.up('.x-form-item')); + this.form.remove(c); + // If a Container, remove any Fields it might contain + } else if (c.findByType) { + Ext.each(c.findBy(this.isField), this.form.remove, this.form); + } + }, + + /** + * Starts monitoring of the valid state of this form. Usually this is done by passing the config + * option "monitorValid" + */ + startMonitoring : function(){ + if(!this.validTask){ + this.validTask = new Ext.util.TaskRunner(); + this.validTask.start({ + run : this.bindHandler, + interval : this.monitorPoll || 200, + scope: this + }); + } + }, + + /** + * Stops monitoring of the valid state of this form + */ + stopMonitoring : function(){ + if(this.validTask){ + this.validTask.stopAll(); + this.validTask = null; + } + }, + + /** + * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call. + * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details) + */ + load : function(){ + this.form.load.apply(this.form, arguments); + }, + + // private + onDisable : function(){ + Ext.FormPanel.superclass.onDisable.call(this); + if(this.form){ + this.form.items.each(function(){ + this.disable(); + }); + } + }, + + // private + onEnable : function(){ + Ext.FormPanel.superclass.onEnable.call(this); + if(this.form){ + this.form.items.each(function(){ + this.enable(); + }); + } + }, + + // private + bindHandler : function(){ + var valid = true; + this.form.items.each(function(f){ + if(!f.isValid(true)){ + valid = false; + return false; + } + }); + if(this.fbar){ + var fitems = this.fbar.items.items; + for(var i = 0, len = fitems.length; i < len; i++){ + var btn = fitems[i]; + if(btn.formBind === true && btn.disabled === valid){ + btn.setDisabled(!valid); + } + } + } + this.fireEvent('clientvalidation', this, valid); + } +}); +Ext.reg('form', Ext.FormPanel); + +Ext.form.FormPanel = Ext.FormPanel; +