]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/form/Panel.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / form / Panel.js
CommitLineData
6527f429
DM
1/**\r
2 * FormPanel provides a standard container for forms. It is essentially a standard {@link Ext.panel.Panel} which\r
3 * automatically creates a {@link Ext.form.Basic BasicForm} for managing any {@link Ext.form.field.Field}\r
4 * objects that are added as descendants of the panel. It also includes conveniences for configuring and\r
5 * working with the BasicForm and the collection of Fields.\r
6 * \r
7 * # Layout\r
8 * \r
9 * By default, FormPanel is configured with `{@link Ext.layout.container.Anchor layout:'anchor'}` for\r
10 * the layout of its immediate child items. This can be changed to any of the supported container layouts.\r
11 * The layout of sub-containers is configured in {@link Ext.container.Container#layout the standard way}.\r
12 * \r
13 * # BasicForm\r
14 * \r
15 * FormPanel class accepts all\r
16 * of the config options supported by the {@link Ext.form.Basic} class, and will pass them along to\r
17 * the internal BasicForm when it is created.\r
18 * \r
19 * The following events fired by the BasicForm will be re-fired by the FormPanel and can therefore be\r
20 * listened for on the FormPanel itself:\r
21 * \r
22 * - {@link Ext.form.Basic#beforeaction beforeaction}\r
23 * - {@link Ext.form.Basic#actionfailed actionfailed}\r
24 * - {@link Ext.form.Basic#actioncomplete actioncomplete}\r
25 * - {@link Ext.form.Basic#validitychange validitychange}\r
26 * - {@link Ext.form.Basic#dirtychange dirtychange}\r
27 * \r
28 * # Field Defaults\r
29 * \r
30 * The {@link #fieldDefaults} config option conveniently allows centralized configuration of default values\r
31 * for all fields added as descendants of the FormPanel. Any config option recognized by implementations\r
32 * of {@link Ext.form.Labelable} may be included in this object. See the {@link #fieldDefaults} documentation\r
33 * for details of how the defaults are applied.\r
34 * \r
35 * # Form Validation\r
36 * \r
37 * With the default configuration, form fields are validated on-the-fly while the user edits their values.\r
38 * This can be controlled on a per-field basis (or via the {@link #fieldDefaults} config) with the field\r
39 * config properties {@link Ext.form.field.Field#validateOnChange} and {@link Ext.form.field.Base#checkChangeEvents},\r
40 * and the FormPanel's config properties {@link #pollForChanges} and {@link #pollInterval}.\r
41 * \r
42 * Any component within the FormPanel can be configured with `formBind: true`. This will cause that\r
43 * component to be automatically disabled when the form is invalid, and enabled when it is valid. This is most\r
44 * commonly used for Button components to prevent submitting the form in an invalid state, but can be used on\r
45 * any component type.\r
46 * \r
47 * For more information on form validation see the following:\r
48 * \r
49 * - {@link Ext.form.field.Field#validateOnChange}\r
50 * - {@link #pollForChanges} and {@link #pollInterval}\r
51 * - {@link Ext.form.field.VTypes}\r
52 * - {@link Ext.form.Basic#doAction BasicForm.doAction clientValidation notes}\r
53 * \r
54 * # Form Submission\r
55 * \r
56 * By default, Ext Forms are submitted through Ajax, using {@link Ext.form.action.Action}. See the documentation for\r
57 * {@link Ext.form.Basic} for details.\r
58 *\r
59 * # Example usage\r
60 * \r
61 * @example\r
62 * Ext.create('Ext.form.Panel', {\r
63 * title: 'Simple Form',\r
64 * bodyPadding: 5,\r
65 * width: 350,\r
66 * \r
67 * // The form will submit an AJAX request to this URL when submitted\r
68 * url: 'save-form.php',\r
69 * \r
70 * // Fields will be arranged vertically, stretched to full width\r
71 * layout: 'anchor',\r
72 * defaults: {\r
73 * anchor: '100%'\r
74 * },\r
75 * \r
76 * // The fields\r
77 * defaultType: 'textfield',\r
78 * items: [{\r
79 * fieldLabel: 'First Name',\r
80 * name: 'first',\r
81 * allowBlank: false\r
82 * },{\r
83 * fieldLabel: 'Last Name',\r
84 * name: 'last',\r
85 * allowBlank: false\r
86 * }],\r
87 * \r
88 * // Reset and Submit buttons\r
89 * buttons: [{\r
90 * text: 'Reset',\r
91 * handler: function() {\r
92 * this.up('form').getForm().reset();\r
93 * }\r
94 * }, {\r
95 * text: 'Submit',\r
96 * formBind: true, //only enabled once the form is valid\r
97 * disabled: true,\r
98 * handler: function() {\r
99 * var form = this.up('form').getForm();\r
100 * if (form.isValid()) {\r
101 * form.submit({\r
102 * success: function(form, action) {\r
103 * Ext.Msg.alert('Success', action.result.msg);\r
104 * },\r
105 * failure: function(form, action) {\r
106 * Ext.Msg.alert('Failed', action.result.msg);\r
107 * }\r
108 * });\r
109 * }\r
110 * }\r
111 * }],\r
112 * renderTo: Ext.getBody()\r
113 * });\r
114 *\r
115 */\r
116Ext.define('Ext.form.Panel', {\r
117 extend:'Ext.panel.Panel',\r
118 mixins: {\r
119 fieldAncestor: 'Ext.form.FieldAncestor'\r
120 },\r
121 alias: 'widget.form',\r
122 alternateClassName: ['Ext.FormPanel', 'Ext.form.FormPanel'],\r
123 requires: ['Ext.form.Basic', 'Ext.util.TaskRunner'],\r
124\r
125 /**\r
126 * @cfg {Boolean} pollForChanges\r
127 * If set to `true`, sets up an interval task (using the {@link #pollInterval}) in which the\r
128 * panel's fields are repeatedly checked for changes in their values. This is in addition to the normal detection\r
129 * each field does on its own input element, and is not needed in most cases. It does, however, provide a\r
130 * means to absolutely guarantee detection of all changes including some edge cases in some browsers which\r
131 * do not fire native events. Defaults to `false`.\r
132 */\r
133\r
134 /**\r
135 * @cfg {Number} pollInterval\r
136 * Interval in milliseconds at which the form's fields are checked for value changes. Only used if\r
137 * the {@link #pollForChanges} option is set to `true`. Defaults to 500 milliseconds.\r
138 */\r
139\r
140 /**\r
141 * @cfg {Ext.enums.Layout/Object} layout\r
142 * The {@link Ext.container.Container#layout} for the form panel's immediate child items.\r
143 */\r
144 layout: 'anchor',\r
145\r
146 bodyAriaRole: 'form',\r
147 \r
148 basicFormConfigs: [\r
149 /**\r
150 * @cfg\r
151 * @inheritdoc Ext.form.Basic#api\r
152 */\r
153 'api', \r
154 /**\r
155 * @cfg\r
156 * @inheritdoc Ext.form.Basic#baseParams\r
157 */\r
158 'baseParams', \r
159 /**\r
160 * @cfg\r
161 * @inheritdoc Ext.form.Basic#errorReader\r
162 */\r
163 'errorReader', \r
164 /**\r
165 * @cfg\r
166 * @inheritdoc Ext.form.Basic#jsonSubmit\r
167 */\r
168 'jsonSubmit',\r
169 /**\r
170 * @cfg\r
171 * @inheritdoc Ext.form.Basic#method\r
172 */\r
173 'method', \r
174 /**\r
175 * @cfg\r
176 * @inheritdoc Ext.form.Basic#paramOrder\r
177 */\r
178 'paramOrder',\r
179 /**\r
180 * @cfg\r
181 * @inheritdoc Ext.form.Basic#paramsAsHash\r
182 */\r
183 'paramsAsHash',\r
184 /**\r
185 * @cfg\r
186 * @inheritdoc Ext.form.Basic#reader\r
187 */\r
188 'reader',\r
189 /**\r
190 * @cfg\r
191 * @inheritdoc Ext.form.Basic#standardSubmit\r
192 */\r
193 'standardSubmit',\r
194 /**\r
195 * @cfg\r
196 * @inheritdoc Ext.form.Basic#timeout\r
197 */\r
198 'timeout',\r
199 /**\r
200 * @cfg\r
201 * @inheritdoc Ext.form.Basic#trackResetOnLoad\r
202 */\r
203 'trackResetOnLoad',\r
204 /**\r
205 * @cfg\r
206 * @inheritdoc Ext.form.Basic#url\r
207 */\r
208 'url',\r
209 /**\r
210 * @cfg\r
211 * @inheritdoc Ext.form.Basic#waitMsgTarget\r
212 */\r
213 'waitMsgTarget',\r
214 /**\r
215 * @cfg\r
216 * @inheritdoc Ext.form.Basic#waitTitle\r
217 */\r
218 'waitTitle'\r
219 ],\r
220\r
221 initComponent: function() {\r
222 var me = this;\r
223\r
224 if (me.frame) {\r
225 me.border = false;\r
226 }\r
227\r
228 me.initFieldAncestor();\r
229 me.callParent();\r
230\r
231 me.relayEvents(me.form, [\r
232 /**\r
233 * @event beforeaction\r
234 * @inheritdoc Ext.form.Basic#beforeaction\r
235 */\r
236 'beforeaction',\r
237 /**\r
238 * @event actionfailed\r
239 * @inheritdoc Ext.form.Basic#actionfailed\r
240 */\r
241 'actionfailed',\r
242 /**\r
243 * @event actioncomplete\r
244 * @inheritdoc Ext.form.Basic#actioncomplete\r
245 */\r
246 'actioncomplete',\r
247 /**\r
248 * @event validitychange\r
249 * @inheritdoc Ext.form.Basic#validitychange\r
250 */\r
251 'validitychange',\r
252 /**\r
253 * @event dirtychange\r
254 * @inheritdoc Ext.form.Basic#dirtychange\r
255 */\r
256 'dirtychange'\r
257 ]);\r
258\r
259 // Start polling if configured\r
260 if (me.pollForChanges) {\r
261 me.startPolling(me.pollInterval || 500);\r
262 }\r
263 },\r
264\r
265 initItems: function() {\r
266 // Create the BasicForm\r
267 this.callParent();\r
268 this.initMonitor();\r
269 this.form = this.createForm();\r
270 },\r
271\r
272 // Initialize the BasicForm after all layouts have been completed.\r
273 afterFirstLayout: function() {\r
274 this.callParent(arguments);\r
275 this.form.initialize();\r
276 },\r
277\r
278 /**\r
279 * @private\r
280 */\r
281 createForm: function() {\r
282 var cfg = {},\r
283 props = this.basicFormConfigs,\r
284 len = props.length,\r
285 i = 0,\r
286 prop;\r
287 \r
288 for (; i < len; ++i) {\r
289 prop = props[i];\r
290 cfg[prop] = this[prop];\r
291 }\r
292 return new Ext.form.Basic(this, cfg);\r
293 },\r
294\r
295 /**\r
296 * Provides access to the {@link Ext.form.Basic Form} which this Panel contains.\r
297 * @return {Ext.form.Basic} The {@link Ext.form.Basic Form} which this Panel contains.\r
298 */\r
299 getForm: function() {\r
300 return this.form;\r
301 },\r
302\r
303 /**\r
304 * Loads an {@link Ext.data.Model} into this form (internally just calls {@link Ext.form.Basic#loadRecord})\r
305 * See also {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}. The fields in the model are mapped to \r
306 * fields in the form by matching either the {@link Ext.form.field.Base#name} or {@link Ext.Component#itemId}. \r
307 * @param {Ext.data.Model} record The record to load\r
308 * @return {Ext.form.Basic} The Ext.form.Basic attached to this FormPanel\r
309 */\r
310 loadRecord: function(record) {\r
311 return this.getForm().loadRecord(record);\r
312 },\r
313\r
314 /**\r
315 * Returns the currently loaded Ext.data.Model instance if one was loaded via {@link #loadRecord}.\r
316 * @return {Ext.data.Model} The loaded instance\r
317 */\r
318 getRecord: function() {\r
319 return this.getForm().getRecord();\r
320 },\r
321 \r
322 /**\r
323 * Persists the values in this form into the passed {@link Ext.data.Model} object in a beginEdit/endEdit block.\r
324 * If the record is not specified, it will attempt to update (if it exists) the record provided to {@link #loadRecord}.\r
325 * @param {Ext.data.Model} [record] The record to edit\r
326 * @return {Ext.form.Basic} The Ext.form.Basic attached to this FormPanel\r
327 */\r
328 updateRecord: function(record) {\r
329 return this.getForm().updateRecord(record);\r
330 },\r
331\r
332 /**\r
333 * Convenience function for fetching the current value of each field in the form. This is the same as calling\r
334 * {@link Ext.form.Basic#getValues this.getForm().getValues()}.\r
335 *\r
336 * @inheritdoc Ext.form.Basic#getValues\r
337 */\r
338 getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {\r
339 return this.getForm().getValues(asString, dirtyOnly, includeEmptyText, useDataValues);\r
340 },\r
341 \r
342 /**\r
343 * Convenience function to check if the form has any dirty fields. This is the same as calling\r
344 * {@link Ext.form.Basic#isDirty this.getForm().isDirty()}.\r
345 *\r
346 * @inheritdoc Ext.form.Basic#isDirty\r
347 */\r
348 isDirty: function () {\r
349 return this.form.isDirty();\r
350 },\r
351 \r
352 /**\r
353 * Convenience function to check if the form has all valid fields. This is the same as calling\r
354 * {@link Ext.form.Basic#isValid this.getForm().isValid()}.\r
355 *\r
356 * @inheritdoc Ext.form.Basic#isValid\r
357 */\r
358 isValid: function () {\r
359 return this.form.isValid();\r
360 },\r
361\r
362 /**\r
363 * Convenience function reset the form. This is the same as calling\r
364 * {@link Ext.form.Basic#reset this.getForm().reset()}.\r
365 *\r
366 * @inheritdoc Ext.form.Basic#reset\r
367 */\r
368 reset: function() {\r
369 this.form.reset();\r
370 },\r
371 \r
372 /**\r
373 * Convenience function to check if the form has any invalid fields. This is the same as calling\r
374 * {@link Ext.form.Basic#hasInvalidField this.getForm().hasInvalidField()}.\r
375 *\r
376 * @inheritdoc Ext.form.Basic#hasInvalidField\r
377 */\r
378 hasInvalidField: function () {\r
379 return this.form.hasInvalidField();\r
380 },\r
381\r
382 beforeDestroy: function() {\r
383 this.stopPolling();\r
384 this.form.destroy();\r
385 this.callParent();\r
386 },\r
387\r
388 /**\r
389 * This is a proxy for the underlying BasicForm's {@link Ext.form.Basic#load} call.\r
390 * @param {Object} options The options to pass to the action (see {@link Ext.form.Basic#load} and\r
391 * {@link Ext.form.Basic#doAction} for details)\r
392 */\r
393 load: function(options) {\r
394 this.form.load(options);\r
395 },\r
396\r
397 /**\r
398 * This is a proxy for the underlying BasicForm's {@link Ext.form.Basic#submit} call.\r
399 * @param {Object} options The options to pass to the action (see {@link Ext.form.Basic#submit} and\r
400 * {@link Ext.form.Basic#doAction} for details)\r
401 */\r
402 submit: function(options) {\r
403 this.form.submit(options);\r
404 },\r
405\r
406 /**\r
407 * Start an interval task to continuously poll all the fields in the form for changes in their\r
408 * values. This is normally started automatically by setting the {@link #pollForChanges} config.\r
409 * @param {Number} interval The interval in milliseconds at which the check should run.\r
410 */\r
411 startPolling: function(interval) {\r
412 this.stopPolling();\r
413 var task = new Ext.util.TaskRunner(interval);\r
414 task.start({\r
415 interval: 0,\r
416 run: this.checkChange,\r
417 scope: this\r
418 });\r
419 this.pollTask = task;\r
420 },\r
421\r
422 /**\r
423 * Stop a running interval task that was started by {@link #startPolling}.\r
424 */\r
425 stopPolling: function() {\r
426 var task = this.pollTask;\r
427 if (task) {\r
428 task.stopAll();\r
429 delete this.pollTask;\r
430 }\r
431 },\r
432\r
433 /**\r
434 * Forces each field within the form panel to\r
435 * {@link Ext.form.field.Field#checkChange check if its value has changed}.\r
436 */\r
437 checkChange: function() {\r
438 var fields = this.form.getFields().items,\r
439 f,\r
440 fLen = fields.length;\r
441\r
442 for (f = 0; f < fLen; f++) {\r
443 fields[f].checkChange();\r
444 }\r
445 }\r
446});\r