X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/form/Panel.js diff --git a/src/form/Panel.js b/src/form/Panel.js new file mode 100644 index 00000000..4957dd39 --- /dev/null +++ b/src/form/Panel.js @@ -0,0 +1,317 @@ +/** + * @class Ext.form.Panel + * @extends Ext.panel.Panel + +FormPanel provides a standard container for forms. It is essentially a standard {@link Ext.panel.Panel} which +automatically creates a {@link Ext.form.Basic BasicForm} for managing any {@link Ext.form.field.Field} +objects that are added as descendants of the panel. It also includes conveniences for configuring and +working with the BasicForm and the collection of Fields. + +__Layout__ + +By default, FormPanel is configured with `{@link Ext.layout.container.Anchor layout:'anchor'}` for +the layout of its immediate child items. This can be changed to any of the supported container layouts. +The layout of sub-containers is configured in {@link Ext.container.Container#layout the standard way}. + +__BasicForm__ + +Although **not listed** as configuration options of FormPanel, the FormPanel class accepts all +of the config options supported by the {@link Ext.form.Basic} class, and will pass them along to +the internal BasicForm when it is created. + +**Note**: If subclassing FormPanel, any configuration options for the BasicForm must be applied to +the `initialConfig` property of the FormPanel. Applying {@link Ext.form.Basic BasicForm} +configuration settings to `this` will *not* affect the BasicForm's configuration. + +The following events fired by the BasicForm will be re-fired by the FormPanel and can therefore be +listened for on the FormPanel itself: + +- {@link Ext.form.Basic#beforeaction beforeaction} +- {@link Ext.form.Basic#actionfailed actionfailed} +- {@link Ext.form.Basic#actioncomplete actioncomplete} +- {@link Ext.form.Basic#validitychange validitychange} +- {@link Ext.form.Basic#dirtychange dirtychange} + +__Field Defaults__ + +The {@link #fieldDefaults} config option conveniently allows centralized configuration of default values +for all fields added as descendants of the FormPanel. Any config option recognized by implementations +of {@link Ext.form.Labelable} may be included in this object. See the {@link #fieldDefaults} documentation +for details of how the defaults are applied. + +__Form Validation__ + +With the default configuration, form fields are validated on-the-fly while the user edits their values. +This can be controlled on a per-field basis (or via the {@link #fieldDefaults} config) with the field +config properties {@link Ext.form.field.Field#validateOnChange} and {@link Ext.form.field.Base#checkChangeEvents}, +and the FormPanel's config properties {@link #pollForChanges} and {@link #pollInterval}. + +Any component within the FormPanel can be configured with `formBind: true`. This will cause that +component to be automatically disabled when the form is invalid, and enabled when it is valid. This is most +commonly used for Button components to prevent submitting the form in an invalid state, but can be used on +any component type. + +For more information on form validation see the following: + +- {@link Ext.form.field.Field#validateOnChange} +- {@link #pollForChanges} and {@link #pollInterval} +- {@link Ext.form.field.VTypes} +- {@link Ext.form.Basic#doAction BasicForm.doAction clientValidation notes} + +__Form Submission__ + +By default, Ext Forms are submitted through Ajax, using {@link Ext.form.action.Action}. See the documentation for +{@link Ext.form.Basic} for details. +{@img Ext.form.FormPanel/Ext.form.FormPanel.png Ext.form.FormPanel FormPanel component} +__Example usage:__ + + Ext.create('Ext.form.Panel', { + title: 'Simple Form', + bodyPadding: 5, + width: 350, + + // The form will submit an AJAX request to this URL when submitted + url: 'save-form.php', + + // Fields will be arranged vertically, stretched to full width + layout: 'anchor', + defaults: { + anchor: '100%' + }, + + // The fields + defaultType: 'textfield', + items: [{ + fieldLabel: 'First Name', + name: 'first', + allowBlank: false + },{ + fieldLabel: 'Last Name', + name: 'last', + allowBlank: false + }], + + // Reset and Submit buttons + buttons: [{ + text: 'Reset', + handler: function() { + this.up('form').getForm().reset(); + } + }, { + text: 'Submit', + formBind: true, //only enabled once the form is valid + disabled: true, + handler: function() { + var form = this.up('form').getForm(); + if (form.isValid()) { + form.submit({ + success: function(form, action) { + Ext.Msg.alert('Success', action.result.msg); + }, + failure: function(form, action) { + Ext.Msg.alert('Failed', action.result.msg); + } + }); + } + } + }], + renderTo: Ext.getBody() + }); + + * @constructor + * @param {Object} config Configuration options + * @xtype form + * + * @markdown + * @docauthor Jason Johnston + */ +Ext.define('Ext.form.Panel', { + extend:'Ext.panel.Panel', + mixins: { + fieldAncestor: 'Ext.form.FieldAncestor' + }, + alias: 'widget.form', + alternateClassName: ['Ext.FormPanel', 'Ext.form.FormPanel'], + requires: ['Ext.form.Basic', 'Ext.util.TaskRunner'], + + /** + * @cfg {Boolean} pollForChanges + * If set to true, sets up an interval task (using the {@link #pollInterval}) in which the + * panel's fields are repeatedly checked for changes in their values. This is in addition to the normal detection + * each field does on its own input element, and is not needed in most cases. It does, however, provide a + * means to absolutely guarantee detection of all changes including some edge cases in some browsers which + * do not fire native events. Defaults to false. + */ + + /** + * @cfg {Number} pollInterval + * Interval in milliseconds at which the form's fields are checked for value changes. Only used if + * the {@link #pollForChanges} option is set to true. Defaults to 500 milliseconds. + */ + + /** + * @cfg {String} layout The {@link Ext.container.Container#layout} for the form panel's immediate child items. + * Defaults to 'anchor'. + */ + layout: 'anchor', + + ariaRole: 'form', + + initComponent: function() { + var me = this; + + if (me.frame) { + me.border = false; + } + + me.initFieldAncestor(); + me.callParent(); + + me.relayEvents(me.form, [ + 'beforeaction', + 'actionfailed', + 'actioncomplete', + 'validitychange', + 'dirtychange' + ]); + + // Start polling if configured + if (me.pollForChanges) { + me.startPolling(me.pollInterval || 500); + } + }, + + initItems: function() { + // Create the BasicForm + var me = this; + + me.form = me.createForm(); + me.callParent(); + me.form.initialize(); + }, + + /** + * @private + */ + createForm: function() { + return Ext.create('Ext.form.Basic', this, Ext.applyIf({listeners: {}}, this.initialConfig)); + }, + + /** + * Provides access to the {@link Ext.form.Basic Form} which this Panel contains. + * @return {Ext.form.Basic} The {@link Ext.form.Basic Form} which this Panel contains. + */ + getForm: function() { + return this.form; + }, + + /** + * Loads an {@link Ext.data.Model} into this form (internally just calls {@link Ext.form.Basic#loadRecord}) + * See also {@link #trackResetOnLoad}. + * @param {Ext.data.Model} record The record to load + * @return {Ext.form.Basic} The Ext.form.Basic attached to this FormPanel + */ + loadRecord: function(record) { + return this.getForm().loadRecord(record); + }, + + /** + * Returns the currently loaded Ext.data.Model instance if one was loaded via {@link #loadRecord}. + * @return {Ext.data.Model} The loaded instance + */ + getRecord: function() { + return this.getForm().getRecord(); + }, + + /** + * Convenience function for fetching the current value of each field in the form. This is the same as calling + * {@link Ext.form.Basic#getValues this.getForm().getValues()} + * @return {Object} The current form field values, keyed by field name + */ + getValues: function() { + return this.getForm().getValues(); + }, + + beforeDestroy: function() { + this.stopPolling(); + this.form.destroy(); + this.callParent(); + }, + + /** + * This is a proxy for the underlying BasicForm's {@link Ext.form.Basic#load} call. + * @param {Object} options The options to pass to the action (see {@link Ext.form.Basic#load} and + * {@link Ext.form.Basic#doAction} for details) + */ + load: function(options) { + this.form.load(options); + }, + + /** + * This is a proxy for the underlying BasicForm's {@link Ext.form.Basic#submit} call. + * @param {Object} options The options to pass to the action (see {@link Ext.form.Basic#submit} and + * {@link Ext.form.Basic#doAction} for details) + */ + submit: function(options) { + this.form.submit(options); + }, + + /* + * Inherit docs, not using onDisable because it only gets fired + * when the component is rendered. + */ + disable: function(silent) { + this.callParent(arguments); + this.form.getFields().each(function(field) { + field.disable(); + }); + }, + + /* + * Inherit docs, not using onEnable because it only gets fired + * when the component is rendered. + */ + enable: function(silent) { + this.callParent(arguments); + this.form.getFields().each(function(field) { + field.enable(); + }); + }, + + /** + * Start an interval task to continuously poll all the fields in the form for changes in their + * values. This is normally started automatically by setting the {@link #pollForChanges} config. + * @param {Number} interval The interval in milliseconds at which the check should run. + */ + startPolling: function(interval) { + this.stopPolling(); + var task = Ext.create('Ext.util.TaskRunner', interval); + task.start({ + interval: 0, + run: this.checkChange, + scope: this + }); + this.pollTask = task; + }, + + /** + * Stop a running interval task that was started by {@link #startPolling}. + */ + stopPolling: function() { + var task = this.pollTask; + if (task) { + task.stopAll(); + delete this.pollTask; + } + }, + + /** + * Forces each field within the form panel to + * {@link Ext.form.field.Field#checkChange check if its value has changed}. + */ + checkChange: function() { + this.form.getFields().each(function(field) { + field.checkChange(); + }); + } +});