/** * A class which handles submission of data from {@link Ext.form.Basic Form}s and processes * the returned response. * * Instances of this class are only created by a {@link Ext.form.Basic Form} when * {@link Ext.form.Basic#submit submit}ting. * * # Response Packet Criteria * * A response packet may contain: * * - **`success`** property : Boolean - required. * * - **`errors`** property : Object - optional, contains error messages for invalid fields. * * # JSON Packets * * By default, response packets are assumed to be JSON, so a typical response packet may look * like this: * * { * success: false, * errors: { * clientCode: "Client not found", * portOfLoading: "This field must not be null" * } * } * * Other data may be placed into the response for processing by the {@link Ext.form.Basic}'s * callback or event handler methods. The object decoded from this JSON is available in the * {@link Ext.form.action.Action#result result} property. * * Alternatively, if an {@link Ext.form.Basic#errorReader errorReader} is specified as an * {@link Ext.data.reader.Xml XmlReader}: * * errorReader: new Ext.data.reader.Xml({ * record : 'field', * success: '@success' * }, [ * 'id', 'msg' * ] * ) * * then the results may be sent back in XML format: * * <?xml version="1.0" encoding="UTF-8"?> * <message success="false"> * <errors> * <field> * <id>clientCode</id> * <msg> * <![CDATA[ * Code not found. <br /> * <i>This is a test validation message from the server </i> * ]]> * </msg> * </field> * <field> * <id>portOfLoading</id> * <msg> * <![CDATA[ * Port not found. <br /> * <i>This is a test validation message from the server </i> * ]]> * </msg> * </field> * </errors> * </message> * * Other elements may be placed into the response XML for processing by the {@link Ext.form.Basic}'s * callback or event handler methods. The XML document is available in the * {@link Ext.form.Basic#errorReader errorReader}'s {@link Ext.data.reader.Xml#xmlData xmlData} * property. */Ext.define('Ext.form.action.Submit', { extend: 'Ext.form.action.Action', alternateClassName: 'Ext.form.Action.Submit', alias: 'formaction.submit', type: 'submit', /** * @cfg {Boolean} [clientValidation=true] * Determines whether a Form's fields are validated in a final call * to {@link Ext.form.Basic#isValid isValid} prior to submission. Pass false in the Form's * submit options to prevent this. */ run: function() { var me = this, form = me.form; if (me.clientValidation === false || form.isValid()) { me.doSubmit(); } else { // client validation failed me.failureType = Ext.form.action.Action.CLIENT_INVALID; form.afterAction(me, false); } }, /** * @private * Performs the submit of the form data. */ doSubmit: function() { var me = this, ajaxOptions = Ext.apply(me.createCallback(), { url: me.getUrl(), method: me.getMethod(), headers: me.headers }), form = me.form, jsonSubmit = me.jsonSubmit || form.jsonSubmit, paramsProp = jsonSubmit ? 'jsonData' : 'params', formInfo; // For uploads we need to create an actual form that contains the file upload fields, // and pass that to the ajax call so it can do its iframe-based submit method. if (form.hasUpload()) { formInfo = me.buildForm(); ajaxOptions.form = formInfo.formEl; ajaxOptions.isUpload = true; } else { ajaxOptions[paramsProp] = me.getParams(jsonSubmit); } Ext.Ajax.request(ajaxOptions); if (formInfo) { me.cleanup(formInfo); } }, cleanup: function(formInfo) { var formEl = formInfo.formEl, uploadEls = formInfo.uploadEls, uploadFields = formInfo.uploadFields, len = uploadFields.length, i, field; for (i = 0; i < len; ++i) { field = uploadFields[i]; if (!field.clearOnSubmit) { field.restoreInput(uploadEls[i]); } } if (formEl) { Ext.removeNode(formEl); } }, /** * @private * Builds the full set of parameters from the field values plus any additional * configured params. */ getParams: function(useModelValues) { var falseVal = false, configParams = this.callParent(), fieldParams; fieldParams = this.form.getValues( falseVal, falseVal, this.submitEmptyText !== falseVal, useModelValues, /* isSubmitting */ true ); return Ext.apply({}, fieldParams, configParams); }, /** * @private * Builds a form element containing fields corresponding to all the parameters to be * submitted (everything returned by {@link #getParams}. * * NOTE: the form element is automatically added to the DOM, so any code that uses * it must remove it from the DOM after finishing with it. * * @return {HTMLElement} */ buildForm: function() { var me = this, fieldsSpec = [], formSpec, formEl, basicForm = me.form, params = me.getParams(), uploadFields = [], uploadEls = [], fields = basicForm.getFields().items, i, len = fields.length, field, key, value, v, vLen, el; for (i = 0; i < len; ++i) { field = fields[i]; if (field.isFileUpload()) { uploadFields.push(field); } } for (key in params) { if (params.hasOwnProperty(key)) { value = params[key]; if (Ext.isArray(value)) { vLen = value.length; for (v = 0; v < vLen; v++) { fieldsSpec.push(me.getFieldConfig(key, value[v])); } } else { fieldsSpec.push(me.getFieldConfig(key, value)); } } } formSpec = { tag: 'form', role: 'presentation', action: me.getUrl(), method: me.getMethod(), target: me.target ? (Ext.isString(me.target) ? me.target : Ext.fly(me.target).dom.name) : '_self', style: 'display:none', cn: fieldsSpec }; // <debug> if (!formSpec.target) { Ext.raise('Invalid form target.'); } // </debug> // Set the proper encoding for file uploads if (uploadFields.length) { formSpec.encoding = formSpec.enctype = 'multipart/form-data'; } // Create the form formEl = Ext.DomHelper.append(Ext.getBody(), formSpec); // Special handling for file upload fields: since browser security measures prevent setting // their values programatically, and prevent carrying their selected values over // when cloning, we have to move the actual field instances out of their components // and into the form. len = uploadFields.length; for (i = 0; i < len; ++i) { el = uploadFields[i].extractFileInput(); formEl.appendChild(el); uploadEls.push(el); } return { formEl: formEl, uploadFields: uploadFields, uploadEls: uploadEls }; }, getFieldConfig: function(name, value) { return { tag: 'input', type: 'hidden', name: name, value: Ext.String.htmlEncode(value) }; }, /** * @private */ onSuccess: function(response) { var form = this.form, formActive = form && !form.destroying && !form.destroyed, success = true, result = this.processResponse(response); if (result !== true && !result.success) { if (result.errors && formActive) { form.markInvalid(result.errors); } this.failureType = Ext.form.action.Action.SERVER_INVALID; success = false; } if (formActive) { form.afterAction(this, success); } }, /** * @private */ handleResponse: function(response) { var form = this.form, errorReader = form.errorReader, rs, errors, i, len, records, result; if (errorReader) { rs = errorReader.read(response); records = rs.records; errors = []; if (records) { for (i = 0, len = records.length; i < len; i++) { errors[i] = records[i].data; } } if (errors.length < 1) { errors = null; } result = { success: rs.success, errors: errors }; } else { try { result = Ext.decode(response.responseText); } catch (e) { result = { success: false, errors: [] }; } } return result; }});