commit extjs-2.2.1
[extjs.git] / source / widgets / form / Form.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.form.FormPanel\r
11  * @extends Ext.Panel\r
12  * Standard form container.\r
13  * <p><b>Although they are not listed, this class also accepts all the config options required to configure its internal {@link Ext.form.BasicForm}</b></p>\r
14  * <p>The BasicForm is configured using the {@link #initialConfig} of the FormPanel - that is the configuration object passed to the constructor.\r
15  * This means that if you subclass FormPanel, and you wish to configure the BasicForm, you will need to insert any configuration options\r
16  * for the BasicForm into the <tt><b>initialConfig</b></tt> property. Applying BasicForm configuration settings to <b><tt>this</tt></b> will\r
17  * not affect the BasicForm's configuration.</p>\r
18  * <p>By default, FormPanel uses an {@link Ext.layout.FormLayout} layout manager, which styles and renders fields and labels correctly.\r
19  * When nesting additional Containers within a FormPanel, you should ensure that any descendant Containers which\r
20  * host input Fields use the {@link Ext.layout.FormLayout} layout manager.</p>\r
21  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}.\r
22  * To enable normal browser submission of the Ext Form contained in this FormPanel,\r
23  * use the {@link Ext.form.BasicForm#standardSubmit standardSubmit) option:</p><pre><code>\r
24 var myForm = new Ext.form.FormPanel({\r
25     standardSubmit: true,\r
26     items: myFieldset\r
27 });</code></pre>\r
28  * @constructor\r
29  * @param {Object} config Configuration options\r
30  */\r
31 Ext.FormPanel = Ext.extend(Ext.Panel, {\r
32         /**\r
33          * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).\r
34          */\r
35     /**\r
36      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers and can be overridden\r
37      * on any child container (e.g., a fieldset can specify a different labelWidth for its fields).\r
38      */\r
39     /**\r
40      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.\r
41      */\r
42     /**\r
43      * @cfg {Array} buttons\r
44      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>\r
45      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes\r
46      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on\r
47      * the form's valid/invalid state.</p>\r
48      */\r
49     /**\r
50      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")\r
51      */\r
52     buttonAlign:'center',\r
53 \r
54     /**\r
55      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)\r
56      */\r
57     minButtonWidth:75,\r
58 \r
59     /**\r
60      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").\r
61      * This property cascades to child containers and can be overridden on any child container \r
62      * (e.g., a fieldset can specify a different labelAlign for its fields).\r
63      */\r
64     labelAlign:'left',\r
65 \r
66     /**\r
67      * @cfg {Boolean} monitorValid If true, the form monitors its valid state <b>client-side</b> and\r
68      * regularly fires the {@link #clientvalidation} event passing that state.<br>\r
69      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured\r
70      * {@link #button}s which have been configured with <tt>formBind: true<tt> depending\r
71      * on whether the form is valid or not.</p>\r
72      */\r
73     monitorValid : false,\r
74 \r
75     /**\r
76      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)\r
77      */\r
78     monitorPoll : 200,\r
79 \r
80     /**\r
81      * @cfg {String} layout @hide\r
82      */\r
83     layout: 'form',\r
84 \r
85     // private\r
86     initComponent :function(){\r
87         this.form = this.createForm();\r
88 \r
89         this.bodyCfg = {\r
90             tag: 'form',\r
91             cls: this.baseCls + '-body',\r
92             method : this.method || 'POST',\r
93             id : this.formId || Ext.id()\r
94         };\r
95         if(this.fileUpload) {\r
96             this.bodyCfg.enctype = 'multipart/form-data';\r
97         }\r
98 \r
99         Ext.FormPanel.superclass.initComponent.call(this);\r
100 \r
101         this.initItems();\r
102 \r
103         this.addEvents(\r
104             /**\r
105              * @event clientvalidation\r
106              * If the monitorValid config option is true, this event fires repetitively to notify of valid state\r
107              * @param {Ext.form.FormPanel} this\r
108              * @param {Boolean} valid true if the form has passed client-side validation\r
109              */\r
110             'clientvalidation'\r
111         );\r
112 \r
113         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);\r
114     },\r
115 \r
116     // private\r
117     createForm: function(){\r
118         delete this.initialConfig.listeners;\r
119         return new Ext.form.BasicForm(null, this.initialConfig);\r
120     },\r
121 \r
122     // private\r
123     initFields : function(){\r
124         var f = this.form;\r
125         var formPanel = this;\r
126         var fn = function(c){\r
127             if(c.isFormField){\r
128                 f.add(c);\r
129             }else if(c.doLayout && c != formPanel){\r
130                 Ext.applyIf(c, {\r
131                     labelAlign: c.ownerCt.labelAlign,\r
132                     labelWidth: c.ownerCt.labelWidth,\r
133                     itemCls: c.ownerCt.itemCls\r
134                 });\r
135                 if(c.items){\r
136                     c.items.each(fn);\r
137                 }\r
138             }\r
139         }\r
140         this.items.each(fn);\r
141     },\r
142 \r
143     // private\r
144     getLayoutTarget : function(){\r
145         return this.form.el;\r
146     },\r
147 \r
148     /**\r
149      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.\r
150      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.\r
151      */\r
152     getForm : function(){\r
153         return this.form;\r
154     },\r
155 \r
156     // private\r
157     onRender : function(ct, position){\r
158         this.initFields();\r
159 \r
160         Ext.FormPanel.superclass.onRender.call(this, ct, position);\r
161         this.form.initEl(this.body);\r
162     },\r
163     \r
164     // private\r
165     beforeDestroy: function(){\r
166         Ext.FormPanel.superclass.beforeDestroy.call(this);\r
167         this.stopMonitoring();\r
168         Ext.destroy(this.form);\r
169     },\r
170 \r
171     // private\r
172     initEvents : function(){\r
173         Ext.FormPanel.superclass.initEvents.call(this);\r
174         this.items.on('remove', this.onRemove, this);\r
175                 this.items.on('add', this.onAdd, this);\r
176         if(this.monitorValid){ // initialize after render\r
177             this.startMonitoring();\r
178         }\r
179     },\r
180     \r
181     // private\r
182         onAdd : function(ct, c) {\r
183                 if (c.isFormField) {\r
184                         this.form.add(c);\r
185                 }\r
186         },\r
187         \r
188         // private\r
189         onRemove : function(c) {\r
190                 if (c.isFormField) {\r
191                         Ext.destroy(c.container.up('.x-form-item'));\r
192                         this.form.remove(c);\r
193                 }\r
194         },\r
195 \r
196     /**\r
197      * Starts monitoring of the valid state of this form. Usually this is done by passing the config\r
198      * option "monitorValid"\r
199      */\r
200     startMonitoring : function(){\r
201         if(!this.bound){\r
202             this.bound = true;\r
203             Ext.TaskMgr.start({\r
204                 run : this.bindHandler,\r
205                 interval : this.monitorPoll || 200,\r
206                 scope: this\r
207             });\r
208         }\r
209     },\r
210 \r
211     /**\r
212      * Stops monitoring of the valid state of this form\r
213      */\r
214     stopMonitoring : function(){\r
215         this.bound = false;\r
216     },\r
217 \r
218     /**\r
219      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.\r
220      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)\r
221      */\r
222     load : function(){\r
223         this.form.load.apply(this.form, arguments);  \r
224     },\r
225 \r
226     // private\r
227     onDisable : function(){\r
228         Ext.FormPanel.superclass.onDisable.call(this);\r
229         if(this.form){\r
230             this.form.items.each(function(){\r
231                  this.disable();\r
232             });\r
233         }\r
234     },\r
235 \r
236     // private\r
237     onEnable : function(){\r
238         Ext.FormPanel.superclass.onEnable.call(this);\r
239         if(this.form){\r
240             this.form.items.each(function(){\r
241                  this.enable();\r
242             });\r
243         }\r
244     },\r
245 \r
246     // private\r
247     bindHandler : function(){\r
248         if(!this.bound){\r
249             return false; // stops binding\r
250         }\r
251         var valid = true;\r
252         this.form.items.each(function(f){\r
253             if(!f.isValid(true)){\r
254                 valid = false;\r
255                 return false;\r
256             }\r
257         });\r
258         if(this.buttons){\r
259             for(var i = 0, len = this.buttons.length; i < len; i++){\r
260                 var btn = this.buttons[i];\r
261                 if(btn.formBind === true && btn.disabled === valid){\r
262                     btn.setDisabled(!valid);\r
263                 }\r
264             }\r
265         }\r
266         this.fireEvent('clientvalidation', this, valid);\r
267     }\r
268 });\r
269 Ext.reg('form', Ext.FormPanel);\r
270 \r
271 Ext.form.FormPanel = Ext.FormPanel;\r
272 \r