Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / form / Panel.js
diff --git a/src/form/Panel.js b/src/form/Panel.js
new file mode 100644 (file)
index 0000000..4957dd3
--- /dev/null
@@ -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 <jason@sencha.com>
+ */
+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 <tt>true</tt>, 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 <tt>false</tt>.
+     */
+
+    /**
+     * @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 <tt>true</tt>. Defaults to 500 milliseconds.
+     */
+
+    /**
+     * @cfg {String} layout The {@link Ext.container.Container#layout} for the form panel's immediate child items.
+     * Defaults to <tt>'anchor'</tt>.
+     */
+    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();
+        });
+    }
+});