]> git.proxmox.com Git - extjs.git/blob - extjs/classic/classic/src/form/action/Submit.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / form / action / Submit.js
1 /**
2 * A class which handles submission of data from {@link Ext.form.Basic Form}s and processes the returned response.
3 *
4 * Instances of this class are only created by a {@link Ext.form.Basic Form} when
5 * {@link Ext.form.Basic#submit submit}ting.
6 *
7 * # Response Packet Criteria
8 *
9 * A response packet may contain:
10 *
11 * - **`success`** property : Boolean - required.
12 *
13 * - **`errors`** property : Object - optional, contains error messages for invalid fields.
14 *
15 * # JSON Packets
16 *
17 * By default, response packets are assumed to be JSON, so a typical response packet may look like this:
18 *
19 * {
20 * success: false,
21 * errors: {
22 * clientCode: "Client not found",
23 * portOfLoading: "This field must not be null"
24 * }
25 * }
26 *
27 * Other data may be placed into the response for processing by the {@link Ext.form.Basic}'s callback or event handler
28 * methods. The object decoded from this JSON is available in the {@link Ext.form.action.Action#result result} property.
29 *
30 * Alternatively, if an {@link Ext.form.Basic#errorReader errorReader} is specified as an
31 * {@link Ext.data.reader.Xml XmlReader}:
32 *
33 * errorReader: new Ext.data.reader.Xml({
34 * record : 'field',
35 * success: '@success'
36 * }, [
37 * 'id', 'msg'
38 * ]
39 * )
40 *
41 * then the results may be sent back in XML format:
42 *
43 * <?xml version="1.0" encoding="UTF-8"?>
44 * <message success="false">
45 * <errors>
46 * <field>
47 * <id>clientCode</id>
48 * <msg><![CDATA[Code not found. <br /><i>This is a test validation message from the server </i>]]></msg>
49 * </field>
50 * <field>
51 * <id>portOfLoading</id>
52 * <msg><![CDATA[Port not found. <br /><i>This is a test validation message from the server </i>]]></msg>
53 * </field>
54 * </errors>
55 * </message>
56 *
57 * Other elements may be placed into the response XML for processing by the {@link Ext.form.Basic}'s callback or event
58 * handler methods. The XML document is available in the {@link Ext.form.Basic#errorReader errorReader}'s
59 * {@link Ext.data.reader.Xml#xmlData xmlData} property.
60 */
61 Ext.define('Ext.form.action.Submit', {
62 extend:'Ext.form.action.Action',
63 alternateClassName: 'Ext.form.Action.Submit',
64 alias: 'formaction.submit',
65
66 type: 'submit',
67
68 /**
69 * @cfg {Boolean} [clientValidation=true]
70 * Determines whether a Form's fields are validated in a final call to {@link Ext.form.Basic#isValid isValid} prior
71 * to submission. Pass false in the Form's submit options to prevent this.
72 */
73
74 run : function(){
75 var me = this,
76 form = me.form;
77
78 if (me.clientValidation === false || form.isValid()) {
79 me.doSubmit();
80 } else {
81 // client validation failed
82 me.failureType = Ext.form.action.Action.CLIENT_INVALID;
83 form.afterAction(me, false);
84 }
85 },
86
87 /**
88 * @private
89 * Performs the submit of the form data.
90 */
91 doSubmit: function() {
92 var me = this,
93 ajaxOptions = Ext.apply(me.createCallback(), {
94 url: me.getUrl(),
95 method: me.getMethod(),
96 headers: me.headers
97 }),
98 form = me.form,
99 jsonSubmit = me.jsonSubmit || form.jsonSubmit,
100 paramsProp = jsonSubmit ? 'jsonData' : 'params',
101 formInfo;
102
103 // For uploads we need to create an actual form that contains the file upload fields,
104 // and pass that to the ajax call so it can do its iframe-based submit method.
105 if (form.hasUpload()) {
106 formInfo = me.buildForm();
107 ajaxOptions.form = formInfo.formEl;
108 ajaxOptions.isUpload = true;
109 } else {
110 ajaxOptions[paramsProp] = me.getParams(jsonSubmit);
111 }
112
113 Ext.Ajax.request(ajaxOptions);
114 if (formInfo) {
115 me.cleanup(formInfo);
116 }
117 },
118
119 cleanup: function(formInfo) {
120 var formEl = formInfo.formEl,
121 uploadEls = formInfo.uploadEls,
122 uploadFields = formInfo.uploadFields,
123 len = uploadFields.length,
124 i, field;
125
126 for (i = 0; i < len; ++i) {
127 field = uploadFields[i];
128 if (!field.clearOnSubmit) {
129 field.restoreInput(uploadEls[i]);
130 }
131 }
132
133 if (formEl) {
134 Ext.removeNode(formEl);
135 }
136 },
137
138 /**
139 * @private
140 * Builds the full set of parameters from the field values plus any additional configured params.
141 */
142 getParams: function(useModelValues) {
143 var falseVal = false,
144 configParams = this.callParent(),
145 fieldParams = this.form.getValues(falseVal, falseVal, this.submitEmptyText !== falseVal, useModelValues, /*isSubmitting*/ true);
146 return Ext.apply({}, fieldParams, configParams);
147 },
148
149 /**
150 * @private
151 * Builds a form element containing fields corresponding to all the parameters to be
152 * submitted (everything returned by {@link #getParams}.
153 *
154 * NOTE: the form element is automatically added to the DOM, so any code that uses
155 * it must remove it from the DOM after finishing with it.
156 *
157 * @return {HTMLElement}
158 */
159 buildForm: function() {
160 var me = this,
161 fieldsSpec = [],
162 formSpec,
163 formEl,
164 basicForm = me.form,
165 params = me.getParams(),
166 uploadFields = [],
167 uploadEls = [],
168 fields = basicForm.getFields().items,
169 i,
170 len = fields.length,
171 field, key, value, v, vLen,
172 el;
173
174 for (i = 0; i < len; ++i) {
175 field = fields[i];
176
177 if (field.isFileUpload()) {
178 uploadFields.push(field);
179 }
180 }
181
182 for (key in params) {
183 if (params.hasOwnProperty(key)) {
184 value = params[key];
185
186 if (Ext.isArray(value)) {
187 vLen = value.length;
188 for (v = 0; v < vLen; v++) {
189 fieldsSpec.push(me.getFieldConfig(key, value[v]));
190 }
191 } else {
192 fieldsSpec.push(me.getFieldConfig(key, value));
193 }
194 }
195 }
196
197 formSpec = {
198 tag: 'form',
199 role: 'presentation',
200 action: me.getUrl(),
201 method: me.getMethod(),
202 target: me.target ?
203 (Ext.isString(me.target) ? me.target : Ext.fly(me.target).dom.name) :
204 '_self',
205 style: 'display:none',
206 cn: fieldsSpec
207 };
208
209 // <debug>
210 if (!formSpec.target) {
211 Ext.raise('Invalid form target.');
212 }
213 // </debug>
214
215 // Set the proper encoding for file uploads
216 if (uploadFields.length) {
217 formSpec.encoding = formSpec.enctype = 'multipart/form-data';
218 }
219
220 // Create the form
221 formEl = Ext.DomHelper.append(Ext.getBody(), formSpec);
222
223 // Special handling for file upload fields: since browser security measures prevent setting
224 // their values programatically, and prevent carrying their selected values over when cloning,
225 // we have to move the actual field instances out of their components and into the form.
226 len = uploadFields.length;
227
228 for (i = 0; i < len; ++i) {
229 el = uploadFields[i].extractFileInput();
230 formEl.appendChild(el);
231 uploadEls.push(el);
232 }
233
234 return {
235 formEl: formEl,
236 uploadFields: uploadFields,
237 uploadEls: uploadEls
238 };
239 },
240
241 getFieldConfig: function(name, value) {
242 return {
243 tag: 'input',
244 type: 'hidden',
245 name: name,
246 value: Ext.String.htmlEncode(value)
247 };
248 },
249
250 /**
251 * @private
252 */
253 onSuccess: function(response) {
254 var form = this.form,
255 formActive = form && !form.destroying && !form.destroyed,
256 success = true,
257 result = this.processResponse(response);
258
259 if (result !== true && !result.success) {
260 if (result.errors && formActive) {
261 form.markInvalid(result.errors);
262 }
263 this.failureType = Ext.form.action.Action.SERVER_INVALID;
264 success = false;
265 }
266
267 if (formActive) {
268 form.afterAction(this, success);
269 }
270 },
271
272 /**
273 * @private
274 */
275 handleResponse: function(response) {
276 var form = this.form,
277 errorReader = form.errorReader,
278 rs, errors, i, len, records, result;
279
280 if (errorReader) {
281 rs = errorReader.read(response);
282 records = rs.records;
283 errors = [];
284 if (records) {
285 for(i = 0, len = records.length; i < len; i++) {
286 errors[i] = records[i].data;
287 }
288 }
289 if (errors.length < 1) {
290 errors = null;
291 }
292 result = {
293 success : rs.success,
294 errors : errors
295 };
296 } else {
297 try {
298 result = Ext.decode(response.responseText);
299 } catch (e) {
300 result = {
301 success: false,
302 errors: []
303 };
304 }
305
306 }
307 return result;
308 }
309 });