/**
* 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;
}
});