]>
Commit | Line | Data |
---|---|---|
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 | |
116 | Ext.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 |