3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.form.Basic
17 * @extends Ext.util.Observable
19 * Provides input field management, validation, submission, and form loading services for the collection
20 * of {@link Ext.form.field.Field Field} instances within a {@link Ext.container.Container}. It is recommended
21 * that you use a {@link Ext.form.Panel} as the form container, as that has logic to automatically
22 * hook up an instance of {@link Ext.form.Basic} (plus other conveniences related to field configuration.)
26 * The Basic class delegates the handling of form loads and submits to instances of {@link Ext.form.action.Action}.
27 * See the various Action implementations for specific details of each one's functionality, as well as the
28 * documentation for {@link #doAction} which details the configuration options that can be specified in
31 * The default submit Action is {@link Ext.form.action.Submit}, which uses an Ajax request to submit the
32 * form's values to a configured URL. To enable normal browser submission of an Ext form, use the
33 * {@link #standardSubmit} config option.
35 * Note: File uploads are not performed using normal 'Ajax' techniques; see the description for
36 * {@link #hasUpload} for details.
40 * Ext.create('Ext.form.Panel', {
41 * title: 'Basic Form',
42 * renderTo: Ext.getBody(),
46 * // Any configuration items here will be automatically passed along to
47 * // the Ext.form.Basic instance when it gets created.
49 * // The form will submit an AJAX request to this URL when submitted
50 * url: 'save-form.php',
53 * fieldLabel: 'Field',
59 * handler: function() {
60 * // The getForm() method returns the Ext.form.Basic instance:
61 * var form = this.up('form').getForm();
62 * if (form.isValid()) {
63 * // Submit the Ajax request and handle the response
65 * success: function(form, action) {
66 * Ext.Msg.alert('Success', action.result.msg);
68 * failure: function(form, action) {
69 * Ext.Msg.alert('Failed', action.result.msg);
77 * @docauthor Jason Johnston <jason@sencha.com>
79 Ext.define('Ext.form.Basic', {
80 extend: 'Ext.util.Observable',
81 alternateClassName: 'Ext.form.BasicForm',
82 requires: ['Ext.util.MixedCollection', 'Ext.form.action.Load', 'Ext.form.action.Submit',
83 'Ext.window.MessageBox', 'Ext.data.Errors', 'Ext.util.DelayedTask'],
87 * @param {Ext.container.Container} owner The component that is the container for the form, usually a {@link Ext.form.Panel}
88 * @param {Object} config Configuration options. These are normally specified in the config to the
89 * {@link Ext.form.Panel} constructor, which passes them along to the BasicForm automatically.
91 constructor: function(owner, config) {
93 onItemAddOrRemove = me.onItemAddOrRemove;
97 * @type Ext.container.Container
98 * The container component to which this BasicForm is attached.
102 // Listen for addition/removal of fields in the owner container
104 add: onItemAddOrRemove,
105 remove: onItemAddOrRemove,
109 Ext.apply(me, config);
111 // Normalize the paramOrder to an Array
112 if (Ext.isString(me.paramOrder)) {
113 me.paramOrder = me.paramOrder.split(/[\s,|]/);
116 me.checkValidityTask = Ext.create('Ext.util.DelayedTask', me.checkValidity, me);
120 * @event beforeaction
121 * Fires before any action is performed. Return false to cancel the action.
122 * @param {Ext.form.Basic} this
123 * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} to be performed
127 * @event actionfailed
128 * Fires when an action fails.
129 * @param {Ext.form.Basic} this
130 * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that failed
134 * @event actioncomplete
135 * Fires when an action is completed.
136 * @param {Ext.form.Basic} this
137 * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that completed
141 * @event validitychange
142 * Fires when the validity of the entire form changes.
143 * @param {Ext.form.Basic} this
144 * @param {Boolean} valid <tt>true</tt> if the form is now valid, <tt>false</tt> if it is now invalid.
149 * Fires when the dirty state of the entire form changes.
150 * @param {Ext.form.Basic} this
151 * @param {Boolean} dirty <tt>true</tt> if the form is now dirty, <tt>false</tt> if it is no longer dirty.
159 * Do any post constructor initialization
162 initialize: function(){
163 this.initialized = true;
164 this.onValidityChange(!this.hasInvalidField());
168 * @cfg {String} method
169 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
172 * @cfg {Ext.data.reader.Reader} reader
173 * An Ext.data.DataReader (e.g. {@link Ext.data.reader.Xml}) to be used to read
174 * data when executing 'load' actions. This is optional as there is built-in
175 * support for processing JSON responses.
178 * @cfg {Ext.data.reader.Reader} errorReader
179 * <p>An Ext.data.DataReader (e.g. {@link Ext.data.reader.Xml}) to be used to
180 * read field error messages returned from 'submit' actions. This is optional
181 * as there is built-in support for processing JSON responses.</p>
182 * <p>The Records which provide messages for the invalid Fields must use the
183 * Field name (or id) as the Record ID, and must contain a field called 'msg'
184 * which contains the error message.</p>
185 * <p>The errorReader does not have to be a full-blown implementation of a
186 * Reader. It simply needs to implement a <tt>read(xhr)</tt> function
187 * which returns an Array of Records in an object with the following
188 * structure:</p><pre><code>
197 * The URL to use for form actions if one isn't supplied in the
198 * {@link #doAction doAction} options.
202 * @cfg {Object} baseParams
203 * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
204 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode Ext.Object.toQueryString}.</p>
208 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
213 * @cfg {Object} api (Optional) If specified, load and submit actions will be handled
214 * with {@link Ext.form.action.DirectLoad} and {@link Ext.form.action.DirectLoad}.
215 * Methods which have been imported by {@link Ext.direct.Manager} can be specified here to load and submit
217 * Such as the following:<pre><code>
219 load: App.ss.MyProfile.load,
220 submit: App.ss.MyProfile.submit
223 * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
224 * to customize how the load method is invoked.
225 * Submit actions will always use a standard form submit. The <tt>formHandler</tt> configuration must
226 * be set on the associated server-side method which has been imported by {@link Ext.direct.Manager}.</p>
230 * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
231 * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
232 * <code>load</code> configuration.</p>
233 * <p>Specify the params in the order in which they must be executed on the
234 * server-side as either (1) an Array of String values, or (2) a String of params
235 * delimited by either whitespace, comma, or pipe. For example,
236 * any of the following would be acceptable:</p><pre><code>
237 paramOrder: ['param1','param2','param3']
238 paramOrder: 'param1 param2 param3'
239 paramOrder: 'param1,param2,param3'
240 paramOrder: 'param1|param2|param'
245 * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
246 * <code>load</code> configuration. If <tt>true</tt>, parameters will be sent as a
247 * single hash collection of named arguments (defaults to <tt>false</tt>). Providing a
248 * <tt>{@link #paramOrder}</tt> nullifies this configuration.
253 * @cfg {String} waitTitle
254 * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
256 waitTitle: 'Please Wait...',
259 * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
260 * or {@link #setValues}() data instead of when the form was first created. Defaults to <tt>false</tt>.
262 trackResetOnLoad: false,
265 * @cfg {Boolean} standardSubmit
266 * <p>If set to <tt>true</tt>, a standard HTML form submit is used instead
267 * of a XHR (Ajax) style form submission. Defaults to <tt>false</tt>. All of
268 * the field values, plus any additional params configured via {@link #baseParams}
269 * and/or the <code>options</code> to {@link #submit}, will be included in the
270 * values submitted in the form.</p>
274 * @cfg {Mixed} waitMsgTarget
275 * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
276 * element by passing it or its id or mask the form itself by passing in true. Defaults to <tt>undefined</tt>.
285 * Destroys this object.
287 destroy: function() {
288 this.clearListeners();
289 this.checkValidityTask.cancel();
294 * Handle addition or removal of descendant items. Invalidates the cached list of fields
295 * so that {@link #getFields} will do a fresh query next time it is called. Also adds listeners
296 * for state change events on added fields, and tracks components with formBind=true.
298 onItemAddOrRemove: function(parent, child) {
300 isAdding = !!child.ownerCt,
301 isContainer = child.isContainer;
303 function handleField(field) {
304 // Listen for state change events on fields
305 me[isAdding ? 'mon' : 'mun'](field, {
306 validitychange: me.checkValidity,
307 dirtychange: me.checkDirty,
309 buffer: 100 //batch up sequential calls to avoid excessive full-form validation
311 // Flush the cached list of fields
315 if (child.isFormField) {
318 else if (isContainer) {
320 Ext.Array.forEach(child.query('[isFormField]'), handleField);
323 // Flush the cached list of formBind components
324 delete this._boundItems;
326 // Check form bind, but only after initial add. Batch it to prevent excessive validation
327 // calls when many fields are being added at once.
328 if (me.initialized) {
329 me.checkValidityTask.delay(10);
334 * Return all the {@link Ext.form.field.Field} components in the owner container.
335 * @return {Ext.util.MixedCollection} Collection of the Field objects
337 getFields: function() {
338 var fields = this._fields;
340 fields = this._fields = Ext.create('Ext.util.MixedCollection');
341 fields.addAll(this.owner.query('[isFormField]'));
346 getBoundItems: function() {
347 var boundItems = this._boundItems;
349 boundItems = this._boundItems = Ext.create('Ext.util.MixedCollection');
350 boundItems.addAll(this.owner.query('[formBind]'));
356 * Returns true if the form contains any invalid fields. No fields will be marked as invalid
357 * as a result of calling this; to trigger marking of fields use {@link #isValid} instead.
359 hasInvalidField: function() {
360 return !!this.getFields().findBy(function(field) {
361 var preventMark = field.preventMark,
363 field.preventMark = true;
364 isValid = field.isValid();
365 field.preventMark = preventMark;
371 * Returns true if client-side validation on the form is successful. Any invalid fields will be
372 * marked as invalid. If you only want to determine overall form validity without marking anything,
373 * use {@link #hasInvalidField} instead.
376 isValid: function() {
379 me.batchLayouts(function() {
380 invalid = me.getFields().filterBy(function(field) {
381 return !field.validate();
384 return invalid.length < 1;
388 * Check whether the validity of the entire form has changed since it was last checked, and
389 * if so fire the {@link #validitychange validitychange} event. This is automatically invoked
390 * when an individual field's validity changes.
392 checkValidity: function() {
394 valid = !me.hasInvalidField();
395 if (valid !== me.wasValid) {
396 me.onValidityChange(valid);
397 me.fireEvent('validitychange', me, valid);
404 * Handle changes in the form's validity. If there are any sub components with
405 * formBind=true then they are enabled/disabled based on the new validity.
406 * @param {Boolean} valid
408 onValidityChange: function(valid) {
409 var boundItems = this.getBoundItems();
411 boundItems.each(function(cmp) {
412 if (cmp.disabled === valid) {
413 cmp.setDisabled(!valid);
420 * <p>Returns true if any fields in this form have changed from their original values.</p>
421 * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
422 * Fields' <em>original values</em> are updated when the values are loaded by {@link #setValues}
423 * or {@link #loadRecord}.</p>
426 isDirty: function() {
427 return !!this.getFields().findBy(function(f) {
433 * Check whether the dirty state of the entire form has changed since it was last checked, and
434 * if so fire the {@link #dirtychange dirtychange} event. This is automatically invoked
435 * when an individual field's dirty state changes.
437 checkDirty: function() {
438 var dirty = this.isDirty();
439 if (dirty !== this.wasDirty) {
440 this.fireEvent('dirtychange', this, dirty);
441 this.wasDirty = dirty;
446 * <p>Returns true if the form contains a file upload field. This is used to determine the
447 * method for submitting the form: File uploads are not performed using normal 'Ajax' techniques,
448 * that is they are <b>not</b> performed using XMLHttpRequests. Instead a hidden <tt><form></tt>
449 * element containing all the fields is created temporarily and submitted with its
450 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
451 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
452 * but removed after the return data has been gathered.</p>
453 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
454 * server is using JSON to send the return object, then the
455 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
456 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
457 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
458 * "<" as "&lt;", "&" as "&amp;" etc.</p>
459 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
460 * is created containing a <tt>responseText</tt> property in order to conform to the
461 * requirements of event handlers and callbacks.</p>
462 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
463 * and some server technologies (notably JEE) may require some custom processing in order to
464 * retrieve parameter names and parameter values from the packet content.</p>
467 hasUpload: function() {
468 return !!this.getFields().findBy(function(f) {
469 return f.isFileUpload();
474 * Performs a predefined action (an implementation of {@link Ext.form.action.Action})
475 * to perform application-specific processing.
476 * @param {String/Ext.form.action.Action} action The name of the predefined action type,
477 * or instance of {@link Ext.form.action.Action} to perform.
478 * @param {Object} options (optional) The options to pass to the {@link Ext.form.action.Action}
479 * that will get created, if the <tt>action</tt> argument is a String.
480 * <p>All of the config options listed below are supported by both the
481 * {@link Ext.form.action.Submit submit} and {@link Ext.form.action.Load load}
482 * actions unless otherwise noted (custom actions could also accept
483 * other config options):</p><ul>
485 * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
486 * to the form's {@link #url}.)</div></li>
488 * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
489 * to the form's method, or POST if not defined)</div></li>
491 * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
492 * (defaults to the form's baseParams, or none if not defined)</p>
493 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode Ext.Object.toQueryString}.</p></div></li>
495 * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action.</div></li>
497 * <li><b>success</b> : Function<div class="sub-desc">The callback that will
498 * be invoked after a successful response (see top of
499 * {@link Ext.form.action.Submit submit} and {@link Ext.form.action.Load load}
500 * for a description of what constitutes a successful response).
501 * The function is passed the following parameters:<ul>
502 * <li><tt>form</tt> : The {@link Ext.form.Basic} that requested the action.</li>
503 * <li><tt>action</tt> : The {@link Ext.form.action.Action Action} object which performed the operation.
504 * <div class="sub-desc">The action object contains these properties of interest:<ul>
505 * <li><tt>{@link Ext.form.action.Action#response response}</tt></li>
506 * <li><tt>{@link Ext.form.action.Action#result result}</tt> : interrogate for custom postprocessing</li>
507 * <li><tt>{@link Ext.form.action.Action#type type}</tt></li>
508 * </ul></div></li></ul></div></li>
510 * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
511 * failed transaction attempt. The function is passed the following parameters:<ul>
512 * <li><tt>form</tt> : The {@link Ext.form.Basic} that requested the action.</li>
513 * <li><tt>action</tt> : The {@link Ext.form.action.Action Action} object which performed the operation.
514 * <div class="sub-desc">The action object contains these properties of interest:<ul>
515 * <li><tt>{@link Ext.form.action.Action#failureType failureType}</tt></li>
516 * <li><tt>{@link Ext.form.action.Action#response response}</tt></li>
517 * <li><tt>{@link Ext.form.action.Action#result result}</tt> : interrogate for custom postprocessing</li>
518 * <li><tt>{@link Ext.form.action.Action#type type}</tt></li>
519 * </ul></div></li></ul></div></li>
521 * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
522 * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
524 * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
525 * Determines whether a Form's fields are validated in a final call to
526 * {@link Ext.form.Basic#isValid isValid} prior to submission. Set to <tt>false</tt>
527 * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
529 * @return {Ext.form.Basic} this
531 doAction: function(action, options) {
532 if (Ext.isString(action)) {
533 action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {form: this}));
535 if (this.fireEvent('beforeaction', this, action) !== false) {
536 this.beforeAction(action);
537 Ext.defer(action.run, 100, action);
543 * Shortcut to {@link #doAction do} a {@link Ext.form.action.Submit submit action}. This will use the
544 * {@link Ext.form.action.Submit AJAX submit action} by default. If the {@link #standardsubmit} config is
545 * enabled it will use a standard form element to submit, or if the {@link #api} config is present it will
546 * use the {@link Ext.form.action.DirectLoad Ext.direct.Direct submit action}.
547 * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
548 * <p>The following code:</p><pre><code>
549 myFormPanel.getForm().submit({
550 clientValidation: true,
551 url: 'updateConsignment.php',
553 newStatus: 'delivered'
555 success: function(form, action) {
556 Ext.Msg.alert('Success', action.result.msg);
558 failure: function(form, action) {
559 switch (action.failureType) {
560 case Ext.form.action.Action.CLIENT_INVALID:
561 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
563 case Ext.form.action.Action.CONNECT_FAILURE:
564 Ext.Msg.alert('Failure', 'Ajax communication failed');
566 case Ext.form.action.Action.SERVER_INVALID:
567 Ext.Msg.alert('Failure', action.result.msg);
572 * would process the following server response for a successful submission:<pre><code>
574 "success":true, // note this is Boolean, not string
575 "msg":"Consignment updated"
578 * and the following server response for a failed submission:<pre><code>
580 "success":false, // note this is Boolean, not string
581 "msg":"You do not have permission to perform this operation"
584 * @return {Ext.form.Basic} this
586 submit: function(options) {
587 return this.doAction(this.standardSubmit ? 'standardsubmit' : this.api ? 'directsubmit' : 'submit', options);
591 * Shortcut to {@link #doAction do} a {@link Ext.form.action.Load load action}.
592 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
593 * @return {Ext.form.Basic} this
595 load: function(options) {
596 return this.doAction(this.api ? 'directload' : 'load', options);
600 * Persists the values in this form into the passed {@link Ext.data.Model} object in a beginEdit/endEdit block.
601 * @param {Ext.data.Record} record The record to edit
602 * @return {Ext.form.Basic} this
604 updateRecord: function(record) {
605 var fields = record.fields,
606 values = this.getFieldValues(),
610 fields.each(function(f) {
612 if (name in values) {
613 obj[name] = values[name];
625 * Loads an {@link Ext.data.Model} into this form by calling {@link #setValues} with the
626 * {@link Ext.data.Model#data record data}.
627 * See also {@link #trackResetOnLoad}.
628 * @param {Ext.data.Model} record The record to load
629 * @return {Ext.form.Basic} this
631 loadRecord: function(record) {
632 this._record = record;
633 return this.setValues(record.data);
637 * Returns the last Ext.data.Model instance that was loaded via {@link #loadRecord}
638 * @return {Ext.data.Model} The record
640 getRecord: function() {
646 * Called before an action is performed via {@link #doAction}.
647 * @param {Ext.form.action.Action} action The Action instance that was invoked
649 beforeAction: function(action) {
650 var waitMsg = action.waitMsg,
651 maskCls = Ext.baseCSSPrefix + 'mask-loading',
654 // Call HtmlEditor's syncValue before actions
655 this.getFields().each(function(f) {
656 if (f.isFormField && f.syncValue) {
662 waitMsgTarget = this.waitMsgTarget;
663 if (waitMsgTarget === true) {
664 this.owner.el.mask(waitMsg, maskCls);
665 } else if (waitMsgTarget) {
666 waitMsgTarget = this.waitMsgTarget = Ext.get(waitMsgTarget);
667 waitMsgTarget.mask(waitMsg, maskCls);
669 Ext.MessageBox.wait(waitMsg, action.waitTitle || this.waitTitle);
676 * Called after an action is performed via {@link #doAction}.
677 * @param {Ext.form.action.Action} action The Action instance that was invoked
678 * @param {Boolean} success True if the action completed successfully, false, otherwise.
680 afterAction: function(action, success) {
681 if (action.waitMsg) {
682 var MessageBox = Ext.MessageBox,
683 waitMsgTarget = this.waitMsgTarget;
684 if (waitMsgTarget === true) {
685 this.owner.el.unmask();
686 } else if (waitMsgTarget) {
687 waitMsgTarget.unmask();
689 MessageBox.updateProgress(1);
697 Ext.callback(action.success, action.scope || action, [this, action]);
698 this.fireEvent('actioncomplete', this, action);
700 Ext.callback(action.failure, action.scope || action, [this, action]);
701 this.fireEvent('actionfailed', this, action);
707 * Find a specific {@link Ext.form.field.Field} in this form by id or name.
708 * @param {String} id The value to search for (specify either a {@link Ext.Component#id id} or
709 * {@link Ext.form.field.Field#getName name or hiddenName}).
710 * @return Ext.form.field.Field The first matching field, or <tt>null</tt> if none was found.
712 findField: function(id) {
713 return this.getFields().findBy(function(f) {
714 return f.id === id || f.getName() === id;
720 * Mark fields in this form invalid in bulk.
721 * @param {Array/Object} errors Either an array in the form <code>[{id:'fieldId', msg:'The message'}, ...]</code>,
722 * an object hash of <code>{id: msg, id2: msg2}</code>, or a {@link Ext.data.Errors} object.
723 * @return {Ext.form.Basic} this
725 markInvalid: function(errors) {
728 function mark(fieldId, msg) {
729 var field = me.findField(fieldId);
731 field.markInvalid(msg);
735 if (Ext.isArray(errors)) {
736 Ext.each(errors, function(err) {
737 mark(err.id, err.msg);
740 else if (errors instanceof Ext.data.Errors) {
741 errors.each(function(err) {
742 mark(err.field, err.message);
746 Ext.iterate(errors, mark);
752 * Set values for fields in this form in bulk.
753 * @param {Array/Object} values Either an array in the form:<pre><code>
754 [{id:'clientName', value:'Fred. Olsen Lines'},
755 {id:'portOfLoading', value:'FXT'},
756 {id:'portOfDischarge', value:'OSL'} ]</code></pre>
757 * or an object hash of the form:<pre><code>
759 clientName: 'Fred. Olsen Lines',
760 portOfLoading: 'FXT',
761 portOfDischarge: 'OSL'
763 * @return {Ext.form.Basic} this
765 setValues: function(values) {
768 function setVal(fieldId, val) {
769 var field = me.findField(fieldId);
772 if (me.trackResetOnLoad) {
773 field.resetOriginalValue();
778 if (Ext.isArray(values)) {
780 Ext.each(values, function(val) {
781 setVal(val.id, val.value);
785 Ext.iterate(values, setVal);
791 * Retrieves the fields in the form as a set of key/value pairs, using their
792 * {@link Ext.form.field.Field#getSubmitData getSubmitData()} method to collect the values.
793 * If multiple fields return values under the same name those values will be combined into an Array.
794 * This is similar to {@link #getFieldValues} except that this method collects only String values for
795 * submission, while getFieldValues collects type-specific data values (e.g. Date objects for date fields.)
796 * @param {Boolean} asString (optional) If true, will return the key/value collection as a single
797 * URL-encoded param string. Defaults to false.
798 * @param {Boolean} dirtyOnly (optional) If true, only fields that are dirty will be included in the result.
800 * @param {Boolean} includeEmptyText (optional) If true, the configured emptyText of empty fields will be used.
802 * @return {String/Object}
804 getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
807 this.getFields().each(function(field) {
808 if (!dirtyOnly || field.isDirty()) {
809 var data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText);
810 if (Ext.isObject(data)) {
811 Ext.iterate(data, function(name, val) {
812 if (includeEmptyText && val === '') {
813 val = field.emptyText || '';
815 if (name in values) {
816 var bucket = values[name],
817 isArray = Ext.isArray;
818 if (!isArray(bucket)) {
819 bucket = values[name] = [bucket];
822 values[name] = bucket.concat(val);
835 values = Ext.Object.toQueryString(values);
841 * Retrieves the fields in the form as a set of key/value pairs, using their
842 * {@link Ext.form.field.Field#getModelData getModelData()} method to collect the values.
843 * If multiple fields return values under the same name those values will be combined into an Array.
844 * This is similar to {@link #getValues} except that this method collects type-specific data values
845 * (e.g. Date objects for date fields) while getValues returns only String values for submission.
846 * @param {Boolean} dirtyOnly (optional) If true, only fields that are dirty will be included in the result.
850 getFieldValues: function(dirtyOnly) {
851 return this.getValues(false, dirtyOnly, false, true);
855 * Clears all invalid field messages in this form.
856 * @return {Ext.form.Basic} this
858 clearInvalid: function() {
860 me.batchLayouts(function() {
861 me.getFields().each(function(f) {
869 * Resets all fields in this form.
870 * @return {Ext.form.Basic} this
874 me.batchLayouts(function() {
875 me.getFields().each(function(f) {
883 * Calls {@link Ext#apply Ext.apply} for all fields in this form with the passed object.
884 * @param {Object} obj The object to be applied
885 * @return {Ext.form.Basic} this
887 applyToFields: function(obj) {
888 this.getFields().each(function(f) {
895 * Calls {@link Ext#applyIf Ext.applyIf} for all field in this form with the passed object.
896 * @param {Object} obj The object to be applied
897 * @return {Ext.form.Basic} this
899 applyIfToFields: function(obj) {
900 this.getFields().each(function(f) {
908 * Utility wrapper that suspends layouts of all field parent containers for the duration of a given
909 * function. Used during full-form validation and resets to prevent huge numbers of layouts.
910 * @param {Function} fn
912 batchLayouts: function(fn) {
914 suspended = new Ext.util.HashMap();
916 // Temporarily suspend layout on each field's immediate owner so we don't get a huge layout cascade
917 me.getFields().each(function(field) {
918 var ownerCt = field.ownerCt;
919 if (!suspended.contains(ownerCt)) {
920 suspended.add(ownerCt);
921 ownerCt.oldSuspendLayout = ownerCt.suspendLayout;
922 ownerCt.suspendLayout = true;
926 // Invoke the function
929 // Un-suspend the container layouts
930 suspended.each(function(id, ct) {
931 ct.suspendLayout = ct.oldSuspendLayout;
932 delete ct.oldSuspendLayout;
935 // Trigger a single layout
936 me.owner.doComponentLayout();