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.
37 * File uploads are not performed using normal 'Ajax' techniques; see the description for
38 * {@link #hasUpload} for details. If you're using file uploads you should read the method description.
42 * Ext.create('Ext.form.Panel', {
43 * title: 'Basic Form',
44 * renderTo: Ext.getBody(),
48 * // Any configuration items here will be automatically passed along to
49 * // the Ext.form.Basic instance when it gets created.
51 * // The form will submit an AJAX request to this URL when submitted
52 * url: 'save-form.php',
55 * fieldLabel: 'Field',
61 * handler: function() {
62 * // The getForm() method returns the Ext.form.Basic instance:
63 * var form = this.up('form').getForm();
64 * if (form.isValid()) {
65 * // Submit the Ajax request and handle the response
67 * success: function(form, action) {
68 * Ext.Msg.alert('Success', action.result.msg);
70 * failure: function(form, action) {
71 * Ext.Msg.alert('Failed', action.result.msg);
79 * @docauthor Jason Johnston <jason@sencha.com>
81 Ext.define('Ext.form.Basic', {
82 extend: 'Ext.util.Observable',
83 alternateClassName: 'Ext.form.BasicForm',
84 requires: ['Ext.util.MixedCollection', 'Ext.form.action.Load', 'Ext.form.action.Submit',
85 'Ext.window.MessageBox', 'Ext.data.Errors', 'Ext.util.DelayedTask'],
89 * @param {Ext.container.Container} owner The component that is the container for the form, usually a {@link Ext.form.Panel}
90 * @param {Object} config Configuration options. These are normally specified in the config to the
91 * {@link Ext.form.Panel} constructor, which passes them along to the BasicForm automatically.
93 constructor: function(owner, config) {
95 onItemAddOrRemove = me.onItemAddOrRemove;
99 * @type Ext.container.Container
100 * The container component to which this BasicForm is attached.
104 // Listen for addition/removal of fields in the owner container
106 add: onItemAddOrRemove,
107 remove: onItemAddOrRemove,
111 Ext.apply(me, config);
113 // Normalize the paramOrder to an Array
114 if (Ext.isString(me.paramOrder)) {
115 me.paramOrder = me.paramOrder.split(/[\s,|]/);
118 me.checkValidityTask = Ext.create('Ext.util.DelayedTask', me.checkValidity, me);
122 * @event beforeaction
123 * Fires before any action is performed. Return false to cancel the action.
124 * @param {Ext.form.Basic} this
125 * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} to be performed
129 * @event actionfailed
130 * Fires when an action fails.
131 * @param {Ext.form.Basic} this
132 * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that failed
136 * @event actioncomplete
137 * Fires when an action is completed.
138 * @param {Ext.form.Basic} this
139 * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that completed
143 * @event validitychange
144 * Fires when the validity of the entire form changes.
145 * @param {Ext.form.Basic} this
146 * @param {Boolean} valid <tt>true</tt> if the form is now valid, <tt>false</tt> if it is now invalid.
151 * Fires when the dirty state of the entire form changes.
152 * @param {Ext.form.Basic} this
153 * @param {Boolean} dirty <tt>true</tt> if the form is now dirty, <tt>false</tt> if it is no longer dirty.
161 * Do any post constructor initialization
164 initialize: function(){
165 this.initialized = true;
166 this.onValidityChange(!this.hasInvalidField());
170 * @cfg {String} method
171 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
175 * @cfg {Ext.data.reader.Reader} reader
176 * An Ext.data.DataReader (e.g. {@link Ext.data.reader.Xml}) to be used to read
177 * data when executing 'load' actions. This is optional as there is built-in
178 * support for processing JSON responses.
182 * @cfg {Ext.data.reader.Reader} errorReader
183 * <p>An Ext.data.DataReader (e.g. {@link Ext.data.reader.Xml}) to be used to
184 * read field error messages returned from 'submit' actions. This is optional
185 * as there is built-in support for processing JSON responses.</p>
186 * <p>The Records which provide messages for the invalid Fields must use the
187 * Field name (or id) as the Record ID, and must contain a field called 'msg'
188 * which contains the error message.</p>
189 * <p>The errorReader does not have to be a full-blown implementation of a
190 * Reader. It simply needs to implement a <tt>read(xhr)</tt> function
191 * which returns an Array of Records in an object with the following
192 * structure:</p><pre><code>
201 * The URL to use for form actions if one isn't supplied in the
202 * {@link #doAction doAction} options.
206 * @cfg {Object} baseParams
207 * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
208 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext.Object#toQueryString}.</p>
212 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
217 * @cfg {Object} api (Optional) If specified, load and submit actions will be handled
218 * with {@link Ext.form.action.DirectLoad} and {@link Ext.form.action.DirectLoad}.
219 * Methods which have been imported by {@link Ext.direct.Manager} can be specified here to load and submit
221 * Such as the following:<pre><code>
223 load: App.ss.MyProfile.load,
224 submit: App.ss.MyProfile.submit
227 * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
228 * to customize how the load method is invoked.
229 * Submit actions will always use a standard form submit. The <tt>formHandler</tt> configuration must
230 * be set on the associated server-side method which has been imported by {@link Ext.direct.Manager}.</p>
234 * @cfg {String/String[]} paramOrder <p>A list of params to be executed server side.
235 * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
236 * <code>load</code> configuration.</p>
237 * <p>Specify the params in the order in which they must be executed on the
238 * server-side as either (1) an Array of String values, or (2) a String of params
239 * delimited by either whitespace, comma, or pipe. For example,
240 * any of the following would be acceptable:</p><pre><code>
241 paramOrder: ['param1','param2','param3']
242 paramOrder: 'param1 param2 param3'
243 paramOrder: 'param1,param2,param3'
244 paramOrder: 'param1|param2|param'
249 * @cfg {Boolean} paramsAsHash
250 * Only used for the <code>{@link #api}</code>
251 * <code>load</code> configuration. If <tt>true</tt>, parameters will be sent as a
252 * single hash collection of named arguments. Providing a
253 * <tt>{@link #paramOrder}</tt> nullifies this configuration.
258 * @cfg {String} waitTitle
259 * The default title to show for the waiting message box
261 waitTitle: 'Please Wait...',
264 * @cfg {Boolean} trackResetOnLoad
265 * If set to true, {@link #reset}() resets to the last loaded or {@link #setValues}() data instead of
266 * when the form was first created.
268 trackResetOnLoad: false,
271 * @cfg {Boolean} standardSubmit
272 * If set to true, a standard HTML form submit is used instead of a XHR (Ajax) style form submission.
273 * All of the field values, plus any additional params configured via {@link #baseParams}
274 * and/or the `options` to {@link #submit}, will be included in the values submitted in the form.
278 * @cfg {String/HTMLElement/Ext.Element} waitMsgTarget
279 * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
280 * element by passing it or its id or mask the form itself by passing in true.
289 * Destroys this object.
291 destroy: function() {
292 this.clearListeners();
293 this.checkValidityTask.cancel();
298 * Handle addition or removal of descendant items. Invalidates the cached list of fields
299 * so that {@link #getFields} will do a fresh query next time it is called. Also adds listeners
300 * for state change events on added fields, and tracks components with formBind=true.
302 onItemAddOrRemove: function(parent, child) {
304 isAdding = !!child.ownerCt,
305 isContainer = child.isContainer;
307 function handleField(field) {
308 // Listen for state change events on fields
309 me[isAdding ? 'mon' : 'mun'](field, {
310 validitychange: me.checkValidity,
311 dirtychange: me.checkDirty,
313 buffer: 100 //batch up sequential calls to avoid excessive full-form validation
315 // Flush the cached list of fields
319 if (child.isFormField) {
321 } else if (isContainer) {
323 if (child.isDestroyed) {
324 // the container is destroyed, this means we may have child fields, so here
325 // we just invalidate all the fields to be sure.
328 Ext.Array.forEach(child.query('[isFormField]'), handleField);
332 // Flush the cached list of formBind components
333 delete this._boundItems;
335 // Check form bind, but only after initial add. Batch it to prevent excessive validation
336 // calls when many fields are being added at once.
337 if (me.initialized) {
338 me.checkValidityTask.delay(10);
343 * Return all the {@link Ext.form.field.Field} components in the owner container.
344 * @return {Ext.util.MixedCollection} Collection of the Field objects
346 getFields: function() {
347 var fields = this._fields;
349 fields = this._fields = Ext.create('Ext.util.MixedCollection');
350 fields.addAll(this.owner.query('[isFormField]'));
357 * Finds and returns the set of all items bound to fields inside this form
358 * @return {Ext.util.MixedCollection} The set of all bound form field items
360 getBoundItems: function() {
361 var boundItems = this._boundItems;
363 if (!boundItems || boundItems.getCount() === 0) {
364 boundItems = this._boundItems = Ext.create('Ext.util.MixedCollection');
365 boundItems.addAll(this.owner.query('[formBind]'));
372 * Returns true if the form contains any invalid fields. No fields will be marked as invalid
373 * as a result of calling this; to trigger marking of fields use {@link #isValid} instead.
375 hasInvalidField: function() {
376 return !!this.getFields().findBy(function(field) {
377 var preventMark = field.preventMark,
379 field.preventMark = true;
380 isValid = field.isValid();
381 field.preventMark = preventMark;
387 * Returns true if client-side validation on the form is successful. Any invalid fields will be
388 * marked as invalid. If you only want to determine overall form validity without marking anything,
389 * use {@link #hasInvalidField} instead.
392 isValid: function() {
395 me.batchLayouts(function() {
396 invalid = me.getFields().filterBy(function(field) {
397 return !field.validate();
400 return invalid.length < 1;
404 * Check whether the validity of the entire form has changed since it was last checked, and
405 * if so fire the {@link #validitychange validitychange} event. This is automatically invoked
406 * when an individual field's validity changes.
408 checkValidity: function() {
410 valid = !me.hasInvalidField();
411 if (valid !== me.wasValid) {
412 me.onValidityChange(valid);
413 me.fireEvent('validitychange', me, valid);
420 * Handle changes in the form's validity. If there are any sub components with
421 * formBind=true then they are enabled/disabled based on the new validity.
422 * @param {Boolean} valid
424 onValidityChange: function(valid) {
425 var boundItems = this.getBoundItems();
427 boundItems.each(function(cmp) {
428 if (cmp.disabled === valid) {
429 cmp.setDisabled(!valid);
436 * <p>Returns true if any fields in this form have changed from their original values.</p>
437 * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
438 * Fields' <em>original values</em> are updated when the values are loaded by {@link #setValues}
439 * or {@link #loadRecord}.</p>
442 isDirty: function() {
443 return !!this.getFields().findBy(function(f) {
449 * Check whether the dirty state of the entire form has changed since it was last checked, and
450 * if so fire the {@link #dirtychange dirtychange} event. This is automatically invoked
451 * when an individual field's dirty state changes.
453 checkDirty: function() {
454 var dirty = this.isDirty();
455 if (dirty !== this.wasDirty) {
456 this.fireEvent('dirtychange', this, dirty);
457 this.wasDirty = dirty;
462 * <p>Returns true if the form contains a file upload field. This is used to determine the
463 * method for submitting the form: File uploads are not performed using normal 'Ajax' techniques,
464 * that is they are <b>not</b> performed using XMLHttpRequests. Instead a hidden <tt><form></tt>
465 * element containing all the fields is created temporarily and submitted with its
466 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
467 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
468 * but removed after the return data has been gathered.</p>
469 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
470 * server is using JSON to send the return object, then the
471 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
472 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
473 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
474 * "<" as "&lt;", "&" as "&amp;" etc.</p>
475 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
476 * is created containing a <tt>responseText</tt> property in order to conform to the
477 * requirements of event handlers and callbacks.</p>
478 * <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>
479 * and some server technologies (notably JEE) may require some custom processing in order to
480 * retrieve parameter names and parameter values from the packet content.</p>
483 hasUpload: function() {
484 return !!this.getFields().findBy(function(f) {
485 return f.isFileUpload();
490 * Performs a predefined action (an implementation of {@link Ext.form.action.Action})
491 * to perform application-specific processing.
492 * @param {String/Ext.form.action.Action} action The name of the predefined action type,
493 * or instance of {@link Ext.form.action.Action} to perform.
494 * @param {Object} options (optional) The options to pass to the {@link Ext.form.action.Action}
495 * that will get created, if the <tt>action</tt> argument is a String.
496 * <p>All of the config options listed below are supported by both the
497 * {@link Ext.form.action.Submit submit} and {@link Ext.form.action.Load load}
498 * actions unless otherwise noted (custom actions could also accept
499 * other config options):</p><ul>
501 * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
502 * to the form's {@link #url}.)</div></li>
504 * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
505 * to the form's method, or POST if not defined)</div></li>
507 * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
508 * (defaults to the form's baseParams, or none if not defined)</p>
509 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode Ext.Object.toQueryString}.</p></div></li>
511 * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action.</div></li>
513 * <li><b>success</b> : Function<div class="sub-desc">The callback that will
514 * be invoked after a successful response (see top of
515 * {@link Ext.form.action.Submit submit} and {@link Ext.form.action.Load load}
516 * for a description of what constitutes a successful response).
517 * The function is passed the following parameters:<ul>
518 * <li><tt>form</tt> : The {@link Ext.form.Basic} that requested the action.</li>
519 * <li><tt>action</tt> : The {@link Ext.form.action.Action Action} object which performed the operation.
520 * <div class="sub-desc">The action object contains these properties of interest:<ul>
521 * <li><tt>{@link Ext.form.action.Action#response response}</tt></li>
522 * <li><tt>{@link Ext.form.action.Action#result result}</tt> : interrogate for custom postprocessing</li>
523 * <li><tt>{@link Ext.form.action.Action#type type}</tt></li>
524 * </ul></div></li></ul></div></li>
526 * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
527 * failed transaction attempt. The function is passed the following parameters:<ul>
528 * <li><tt>form</tt> : The {@link Ext.form.Basic} that requested the action.</li>
529 * <li><tt>action</tt> : The {@link Ext.form.action.Action Action} object which performed the operation.
530 * <div class="sub-desc">The action object contains these properties of interest:<ul>
531 * <li><tt>{@link Ext.form.action.Action#failureType failureType}</tt></li>
532 * <li><tt>{@link Ext.form.action.Action#response response}</tt></li>
533 * <li><tt>{@link Ext.form.action.Action#result result}</tt> : interrogate for custom postprocessing</li>
534 * <li><tt>{@link Ext.form.action.Action#type type}</tt></li>
535 * </ul></div></li></ul></div></li>
537 * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
538 * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
540 * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
541 * Determines whether a Form's fields are validated in a final call to
542 * {@link Ext.form.Basic#isValid isValid} prior to submission. Set to <tt>false</tt>
543 * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
545 * @return {Ext.form.Basic} this
547 doAction: function(action, options) {
548 if (Ext.isString(action)) {
549 action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {form: this}));
551 if (this.fireEvent('beforeaction', this, action) !== false) {
552 this.beforeAction(action);
553 Ext.defer(action.run, 100, action);
559 * Shortcut to {@link #doAction do} a {@link Ext.form.action.Submit submit action}. This will use the
560 * {@link Ext.form.action.Submit AJAX submit action} by default. If the {@link #standardSubmit} config is
561 * enabled it will use a standard form element to submit, or if the {@link #api} config is present it will
562 * use the {@link Ext.form.action.DirectLoad Ext.direct.Direct submit action}.
563 * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
564 * <p>The following code:</p><pre><code>
565 myFormPanel.getForm().submit({
566 clientValidation: true,
567 url: 'updateConsignment.php',
569 newStatus: 'delivered'
571 success: function(form, action) {
572 Ext.Msg.alert('Success', action.result.msg);
574 failure: function(form, action) {
575 switch (action.failureType) {
576 case Ext.form.action.Action.CLIENT_INVALID:
577 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
579 case Ext.form.action.Action.CONNECT_FAILURE:
580 Ext.Msg.alert('Failure', 'Ajax communication failed');
582 case Ext.form.action.Action.SERVER_INVALID:
583 Ext.Msg.alert('Failure', action.result.msg);
588 * would process the following server response for a successful submission:<pre><code>
590 "success":true, // note this is Boolean, not string
591 "msg":"Consignment updated"
594 * and the following server response for a failed submission:<pre><code>
596 "success":false, // note this is Boolean, not string
597 "msg":"You do not have permission to perform this operation"
600 * @return {Ext.form.Basic} this
602 submit: function(options) {
603 return this.doAction(this.standardSubmit ? 'standardsubmit' : this.api ? 'directsubmit' : 'submit', options);
607 * Shortcut to {@link #doAction do} a {@link Ext.form.action.Load load action}.
608 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
609 * @return {Ext.form.Basic} this
611 load: function(options) {
612 return this.doAction(this.api ? 'directload' : 'load', options);
616 * Persists the values in this form into the passed {@link Ext.data.Model} object in a beginEdit/endEdit block.
617 * @param {Ext.data.Model} record The record to edit
618 * @return {Ext.form.Basic} this
620 updateRecord: function(record) {
621 var fields = record.fields,
622 values = this.getFieldValues(),
626 fields.each(function(f) {
628 if (name in values) {
629 obj[name] = values[name];
641 * Loads an {@link Ext.data.Model} into this form by calling {@link #setValues} with the
642 * {@link Ext.data.Model#raw record data}.
643 * See also {@link #trackResetOnLoad}.
644 * @param {Ext.data.Model} record The record to load
645 * @return {Ext.form.Basic} this
647 loadRecord: function(record) {
648 this._record = record;
649 return this.setValues(record.data);
653 * Returns the last Ext.data.Model instance that was loaded via {@link #loadRecord}
654 * @return {Ext.data.Model} The record
656 getRecord: function() {
662 * Called before an action is performed via {@link #doAction}.
663 * @param {Ext.form.action.Action} action The Action instance that was invoked
665 beforeAction: function(action) {
666 var waitMsg = action.waitMsg,
667 maskCls = Ext.baseCSSPrefix + 'mask-loading',
670 // Call HtmlEditor's syncValue before actions
671 this.getFields().each(function(f) {
672 if (f.isFormField && f.syncValue) {
678 waitMsgTarget = this.waitMsgTarget;
679 if (waitMsgTarget === true) {
680 this.owner.el.mask(waitMsg, maskCls);
681 } else if (waitMsgTarget) {
682 waitMsgTarget = this.waitMsgTarget = Ext.get(waitMsgTarget);
683 waitMsgTarget.mask(waitMsg, maskCls);
685 Ext.MessageBox.wait(waitMsg, action.waitTitle || this.waitTitle);
692 * Called after an action is performed via {@link #doAction}.
693 * @param {Ext.form.action.Action} action The Action instance that was invoked
694 * @param {Boolean} success True if the action completed successfully, false, otherwise.
696 afterAction: function(action, success) {
697 if (action.waitMsg) {
698 var MessageBox = Ext.MessageBox,
699 waitMsgTarget = this.waitMsgTarget;
700 if (waitMsgTarget === true) {
701 this.owner.el.unmask();
702 } else if (waitMsgTarget) {
703 waitMsgTarget.unmask();
705 MessageBox.updateProgress(1);
713 Ext.callback(action.success, action.scope || action, [this, action]);
714 this.fireEvent('actioncomplete', this, action);
716 Ext.callback(action.failure, action.scope || action, [this, action]);
717 this.fireEvent('actionfailed', this, action);
723 * Find a specific {@link Ext.form.field.Field} in this form by id or name.
724 * @param {String} id The value to search for (specify either a {@link Ext.Component#id id} or
725 * {@link Ext.form.field.Field#getName name or hiddenName}).
726 * @return Ext.form.field.Field The first matching field, or <tt>null</tt> if none was found.
728 findField: function(id) {
729 return this.getFields().findBy(function(f) {
730 return f.id === id || f.getName() === id;
736 * Mark fields in this form invalid in bulk.
737 * @param {Object/Object[]/Ext.data.Errors} errors
738 * Either an array in the form <code>[{id:'fieldId', msg:'The message'}, ...]</code>,
739 * an object hash of <code>{id: msg, id2: msg2}</code>, or a {@link Ext.data.Errors} object.
740 * @return {Ext.form.Basic} this
742 markInvalid: function(errors) {
745 function mark(fieldId, msg) {
746 var field = me.findField(fieldId);
748 field.markInvalid(msg);
752 if (Ext.isArray(errors)) {
753 Ext.each(errors, function(err) {
754 mark(err.id, err.msg);
757 else if (errors instanceof Ext.data.Errors) {
758 errors.each(function(err) {
759 mark(err.field, err.message);
763 Ext.iterate(errors, mark);
769 * Set values for fields in this form in bulk.
770 * @param {Object/Object[]} values Either an array in the form:<pre><code>
771 [{id:'clientName', value:'Fred. Olsen Lines'},
772 {id:'portOfLoading', value:'FXT'},
773 {id:'portOfDischarge', value:'OSL'} ]</code></pre>
774 * or an object hash of the form:<pre><code>
776 clientName: 'Fred. Olsen Lines',
777 portOfLoading: 'FXT',
778 portOfDischarge: 'OSL'
780 * @return {Ext.form.Basic} this
782 setValues: function(values) {
785 function setVal(fieldId, val) {
786 var field = me.findField(fieldId);
789 if (me.trackResetOnLoad) {
790 field.resetOriginalValue();
795 if (Ext.isArray(values)) {
797 Ext.each(values, function(val) {
798 setVal(val.id, val.value);
802 Ext.iterate(values, setVal);
808 * Retrieves the fields in the form as a set of key/value pairs, using their
809 * {@link Ext.form.field.Field#getSubmitData getSubmitData()} method to collect the values.
810 * If multiple fields return values under the same name those values will be combined into an Array.
811 * This is similar to {@link #getFieldValues} except that this method collects only String values for
812 * submission, while getFieldValues collects type-specific data values (e.g. Date objects for date fields.)
813 * @param {Boolean} asString (optional) If true, will return the key/value collection as a single
814 * URL-encoded param string. Defaults to false.
815 * @param {Boolean} dirtyOnly (optional) If true, only fields that are dirty will be included in the result.
817 * @param {Boolean} includeEmptyText (optional) If true, the configured emptyText of empty fields will be used.
819 * @return {String/Object}
821 getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
824 this.getFields().each(function(field) {
825 if (!dirtyOnly || field.isDirty()) {
826 var data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText);
827 if (Ext.isObject(data)) {
828 Ext.iterate(data, function(name, val) {
829 if (includeEmptyText && val === '') {
830 val = field.emptyText || '';
832 if (name in values) {
833 var bucket = values[name],
834 isArray = Ext.isArray;
835 if (!isArray(bucket)) {
836 bucket = values[name] = [bucket];
839 values[name] = bucket.concat(val);
852 values = Ext.Object.toQueryString(values);
858 * Retrieves the fields in the form as a set of key/value pairs, using their
859 * {@link Ext.form.field.Field#getModelData getModelData()} method to collect the values.
860 * If multiple fields return values under the same name those values will be combined into an Array.
861 * This is similar to {@link #getValues} except that this method collects type-specific data values
862 * (e.g. Date objects for date fields) while getValues returns only String values for submission.
863 * @param {Boolean} dirtyOnly (optional) If true, only fields that are dirty will be included in the result.
867 getFieldValues: function(dirtyOnly) {
868 return this.getValues(false, dirtyOnly, false, true);
872 * Clears all invalid field messages in this form.
873 * @return {Ext.form.Basic} this
875 clearInvalid: function() {
877 me.batchLayouts(function() {
878 me.getFields().each(function(f) {
886 * Resets all fields in this form.
887 * @return {Ext.form.Basic} this
891 me.batchLayouts(function() {
892 me.getFields().each(function(f) {
900 * Calls {@link Ext#apply Ext.apply} for all fields in this form with the passed object.
901 * @param {Object} obj The object to be applied
902 * @return {Ext.form.Basic} this
904 applyToFields: function(obj) {
905 this.getFields().each(function(f) {
912 * Calls {@link Ext#applyIf Ext.applyIf} for all field in this form with the passed object.
913 * @param {Object} obj The object to be applied
914 * @return {Ext.form.Basic} this
916 applyIfToFields: function(obj) {
917 this.getFields().each(function(f) {
925 * Utility wrapper that suspends layouts of all field parent containers for the duration of a given
926 * function. Used during full-form validation and resets to prevent huge numbers of layouts.
927 * @param {Function} fn
929 batchLayouts: function(fn) {
931 suspended = new Ext.util.HashMap();
933 // Temporarily suspend layout on each field's immediate owner so we don't get a huge layout cascade
934 me.getFields().each(function(field) {
935 var ownerCt = field.ownerCt;
936 if (!suspended.contains(ownerCt)) {
937 suspended.add(ownerCt);
938 ownerCt.oldSuspendLayout = ownerCt.suspendLayout;
939 ownerCt.suspendLayout = true;
943 // Invoke the function
946 // Un-suspend the container layouts
947 suspended.each(function(id, ct) {
948 ct.suspendLayout = ct.oldSuspendLayout;
949 delete ct.oldSuspendLayout;
952 // Trigger a single layout
953 me.owner.doComponentLayout();