/**
* Provides input field management, validation, submission, and form loading services for the collection
* of {@link Ext.form.field.Field Field} instances within a {@link Ext.container.Container}. It is recommended
* that you use a {@link Ext.form.Panel} as the form container, as that has logic to automatically
* hook up an instance of {@link Ext.form.Basic} (plus other conveniences related to field configuration.)
*
* ## Form Actions
*
* The Basic class delegates the handling of form loads and submits to instances of {@link Ext.form.action.Action}.
* See the various Action implementations for specific details of each one's functionality, as well as the
* documentation for {@link #doAction} which details the configuration options that can be specified in
* each action call.
*
* The default submit Action is {@link Ext.form.action.Submit}, which uses an Ajax request to submit the
* form's values to a configured URL. To enable normal browser submission of an Ext form, use the
* {@link #standardSubmit} config option.
*
* ## File uploads
*
* File uploads are not performed using normal 'Ajax' techniques; see the description for
* {@link #hasUpload} for details. If you're using file uploads you should read the method description.
*
* ## Example usage:
*
* @example
* Ext.create('Ext.form.Panel', {
* title: 'Basic Form',
* renderTo: Ext.getBody(),
* bodyPadding: 5,
* width: 350,
*
* // Any configuration items here will be automatically passed along to
* // the Ext.form.Basic instance when it gets created.
*
* // The form will submit an AJAX request to this URL when submitted
* url: 'save-form.php',
*
* items: [{
* xtype: 'textfield',
* fieldLabel: 'Field',
* name: 'theField'
* }],
*
* buttons: [{
* text: 'Submit',
* handler: function() {
* // The getForm() method returns the Ext.form.Basic instance:
* var form = this.up('form').getForm();
* if (form.isValid()) {
* // Submit the Ajax request and handle the response
* form.submit({
* success: function(form, action) {
* Ext.Msg.alert('Success', action.result.message);
* },
* failure: function(form, action) {
* Ext.Msg.alert('Failed', action.result ? action.result.message : 'No response');
* }
* });
* }
* }
* }]
* });
*/
Ext.define('Ext.form.Basic', {
extend: 'Ext.util.Observable',
alternateClassName: 'Ext.form.BasicForm',
requires: [
'Ext.util.MixedCollection',
'Ext.form.action.Load',
'Ext.form.action.Submit',
'Ext.window.MessageBox',
'Ext.data.ErrorCollection',
'Ext.util.DelayedTask'
],
// Not a public API config, this is useful when we're unit testing so we can
// turn off the delayed tasks so they fire immediately.
taskDelay: 10,
/**
* @event beforeaction
* Fires before any action is performed. Return false to cancel the action.
* @param {Ext.form.Basic} this
* @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} to be performed
*/
/**
* @event actionfailed
* Fires when an action fails.
* @param {Ext.form.Basic} this
* @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that failed
*/
/**
* @event actioncomplete
* Fires when an action is completed.
* @param {Ext.form.Basic} this
* @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that completed
*/
/**
* @event validitychange
* Fires when the validity of the entire form changes.
* @param {Ext.form.Basic} this
* @param {Boolean} valid `true` if the form is now valid, `false` if it is now invalid.
*/
/**
* @event dirtychange
* Fires when the dirty state of the entire form changes.
* @param {Ext.form.Basic} this
* @param {Boolean} dirty `true` if the form is now dirty, `false` if it is no longer dirty.
*/
/**
* @event errorchange
* Fires when the error of one (or more) of the fields in the form changes.
* @param {Ext.form.Basic} this
*
* @private
*/
/**
* Creates new form.
* @param {Ext.container.Container} owner The component that is the container for the form, usually a {@link Ext.form.Panel}
* @param {Object} config Configuration options. These are normally specified in the config to the
* {@link Ext.form.Panel} constructor, which passes them along to the BasicForm automatically.
*/
constructor: function(owner, config) {
var me = this,
reader;
/**
* @property {Ext.container.Container} owner
* The container component to which this BasicForm is attached.
*/
me.owner = owner;
me.fieldMonitors = {
validitychange: me.checkValidityDelay,
enable: me.checkValidityDelay,
disable: me.checkValidityDelay,
dirtychange: me.checkDirtyDelay,
errorchange: me.checkErrorDelay,
scope: me
};
me.checkValidityTask = new Ext.util.DelayedTask(me.checkValidity, me);
me.checkDirtyTask = new Ext.util.DelayedTask(me.checkDirty, me);
me.checkErrorTask = new Ext.util.DelayedTask(me.checkError, me);
// We use the monitor here as opposed to event bubbling. The problem with bubbling is it doesn't
// let us react to items being added/remove at different places in the hierarchy which may have an
// impact on the dirty/valid state.
me.monitor = new Ext.container.Monitor({
selector: '[isFormField]:not([excludeForm])',
scope: me,
addHandler: me.onFieldAdd,
removeHandler: me.onFieldRemove,
invalidateHandler: me.onMonitorInvalidate
});
me.monitor.bind(owner);
Ext.apply(me, config);
// Normalize the paramOrder to an Array
if (Ext.isString(me.paramOrder)) {
me.paramOrder = me.paramOrder.split(/[\s,|]/);
}
reader = me.reader;
if (reader && !reader.isReader) {
if (typeof reader === 'string') {
reader = {
type: reader
};
}
me.reader = Ext.createByAlias('reader.' + reader.type, reader);
}
reader = me.errorReader;
if (reader && !reader.isReader) {
if (typeof reader === 'string') {
reader = {
type: reader
};
}
me.errorReader = Ext.createByAlias('reader.' + reader.type, reader);
}
me.callParent();
},
/**
* Do any post layout initialization
* @private
*/
initialize : function() {
this.initialized = true;
this.onValidityChange(!this.hasInvalidField());
},
/**
* @cfg {String} method
* The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
*/
/**
* @cfg {Object/Ext.data.reader.Reader} reader
* An Ext.data.reader.Reader (e.g. {@link Ext.data.reader.Xml}) instance or
* configuration to be used to read data when executing 'load' actions. This
* is optional as there is built-in support for processing JSON responses.
*/
/**
* @cfg {Object/Ext.data.reader.Reader} errorReader
* An Ext.data.reader.Reader (e.g. {@link Ext.data.reader.Xml}) instance or
* configuration to be used to read field error messages returned from 'submit' actions.
* This is optional as there is built-in support for processing JSON responses.
*
* The Records which provide messages for the invalid Fields must use the
* Field name (or id) as the Record ID, and must contain a field called 'msg'
* which contains the error message.
*
* The errorReader does not have to be a full-blown implementation of a
* Reader. It simply needs to implement a `read(xhr)` function
* which returns an Array of Records in an object with the following
* structure:
*
* {
* records: recordArray
* }
*/
/**
* @cfg {String} url
* The URL to use for form actions if one isn't supplied in the
* {@link Ext.form.Basic#doAction doAction} options.
*/
/**
* @cfg {Object} baseParams
* Parameters to pass with all requests. e.g. baseParams: `{id: '123', foo: 'bar'}`.
*
* Parameters are encoded as standard HTTP parameters using {@link Ext.Object#toQueryString}.
*/
/**
* @cfg {Number} timeout
* Timeout for form actions in seconds.
*/
timeout: 30,
/**
* @cfg {Object} api
* If specified, load and submit actions will be handled with {@link Ext.form.action.DirectLoad DirectLoad}
* and {@link Ext.form.action.DirectSubmit DirectSubmit}. Methods which have been imported by
* {@link Ext.direct.Manager} can be specified here to load and submit forms. API methods may also be
* specified as strings. See {@link Ext.data.proxy.Direct#directFn}. Such as the following:
*
* api: {
* load: App.ss.MyProfile.load,
* submit: App.ss.MyProfile.submit
* }
*
* Load actions can use {@link #paramOrder} or {@link #paramsAsHash} to customize how the load method
* is invoked. Submit actions will always use a standard form submit. The `formHandler` configuration
* (see Ext.direct.RemotingProvider#action) must be set on the associated server-side method which has
* been imported by {@link Ext.direct.Manager}.
*/
/**
* @cfg {String/String[]} paramOrder
* A list of params to be executed server side. Only used for the {@link #api} `load`
* configuration.
*
* Specify the params in the order in which they must be executed on the
* server-side as either (1) an Array of String values, or (2) a String of params
* delimited by either whitespace, comma, or pipe. For example,
* any of the following would be acceptable:
*
* paramOrder: ['param1','param2','param3']
* paramOrder: 'param1 param2 param3'
* paramOrder: 'param1,param2,param3'
* paramOrder: 'param1|param2|param'
*/
/**
* @cfg {Boolean} paramsAsHash
* Only used for the {@link #api} `load` configuration. If true, parameters will be sent as a
* single hash collection of named arguments. Providing a {@link #paramOrder} nullifies this
* configuration.
*/
paramsAsHash: false,
/**
* @cfg {Object/Array} [metadata]
* Optional metadata to pass with the actions when Ext.Direct {@link #api} is used.
* See {@link Ext.direct.Manager} for more information.
*/
//