2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
6 Code distributed by Google as part of the polymer project is also
7 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
11 @group Polymer Core Elements
13 The `core-ajax` element exposes `XMLHttpRequest` functionality.
17 url="http://gdata.youtube.com/feeds/api/videos/"
18 params='{"alt":"json", "q":"chrome"}'
20 on-core-response="{{handleResponse}}"></core-ajax>
22 With `auto` set to `true`, the element performs a request whenever
23 its `url`, `params` or `body` properties are changed.
25 Note: The `params` attribute must be double quoted JSON.
27 You can trigger a request explicitly by calling `go` on the
34 <link rel=
"import" href=
"core-xhr.html">
35 <polymer-element name=
"core-ajax" hidden
attributes=
"url handleAs auto params response error method headers body contentType withCredentials progress loading">
38 Polymer('core-ajax', {
40 * Fired when a response is received.
42 * @event core-response
46 * Fired when an error is received.
52 * Fired whenever a response or an error is received.
54 * @event core-complete
58 * The URL target of the request.
67 * Specifies what data to store in the `response` property, and
68 * to deliver as `event.response` in `response` events.
72 * `text`: uses `XHR.responseText`.
74 * `xml`: uses `XHR.responseXML`.
76 * `json`: uses `XHR.responseText` parsed as JSON.
78 * `arraybuffer`: uses `XHR.response`.
80 * `blob`: uses `XHR.response`.
82 * `document`: uses `XHR.response`.
91 * If true, automatically performs an Ajax request when either `url` or `params` changes.
100 * Parameters to send to the specified URL, as JSON.
103 * @type string (JSON)
109 * The response for the current request, or null if it hasn't
110 * completed yet or the request resulted in error.
112 * @attribute response
119 * The error for the current request, or null if it hasn't
120 * completed yet or the request resulted in success.
129 * Whether the current request is currently loading.
138 * The progress of the current request.
140 * @attribute progress
141 * @type {loaded: number, total: number, lengthComputable: boolean}
147 * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
157 * HTTP request headers to send.
163 * url="http://somesite.com"
164 * headers='{"X-Requested-With": "XMLHttpRequest"}'
166 * on-core-response="{{handleResponse}}"></core-ajax>
175 * Optional raw body content to send when method === "POST".
179 * <core-ajax method="POST" auto url="http://somesite.com"
180 * body='{"foo":1, "bar":2}'>
190 * Content type to use when sending data.
192 * @attribute contentType
194 * @default 'application/x-www-form-urlencoded'
196 contentType
: 'application/x-www-form-urlencoded',
199 * Set the withCredentials flag on the request.
201 * @attribute withCredentials
205 withCredentials
: false,
208 * Additional properties to send to core-xhr.
210 * Can be set to an object containing default properties
211 * to send as arguments to the `core-xhr.request()` method
212 * which implements the low-level communication.
220 created: function() {
225 this.xhr
= document
.createElement('core-xhr');
228 receive: function(response
, xhr
) {
229 if (this.isSuccess(xhr
)) {
230 this.processResponse(xhr
);
232 this.processError(xhr
);
237 isSuccess: function(xhr
) {
238 var status
= xhr
.status
|| 0;
239 return !status
|| (status
>= 200 && status
< 300);
242 processResponse: function(xhr
) {
243 var response
= this.evalResponse(xhr
);
244 if (xhr
=== this.activeRequest
) {
245 this.response
= response
;
247 this.fire('core-response', {response
: response
, xhr
: xhr
});
250 processError: function(xhr
) {
251 var response
= xhr
.status
+ ': ' + xhr
.responseText
;
252 if (xhr
=== this.activeRequest
) {
253 this.error
= response
;
255 this.fire('core-error', {response
: response
, xhr
: xhr
});
258 processProgress: function(progress
, xhr
) {
259 if (xhr
!== this.activeRequest
) {
262 // We create a proxy object here because these fields
263 // on the progress event are readonly properties, which
264 // causes problems in common use cases (e.g. binding to
265 // <paper-progress> attributes).
266 var progressProxy
= {
267 lengthComputable
: progress
.lengthComputable
,
268 loaded
: progress
.loaded
,
269 total
: progress
.total
271 this.progress
= progressProxy
;
274 complete: function(xhr
) {
275 if (xhr
=== this.activeRequest
) {
276 this.loading
= false;
278 this.fire('core-complete', {response
: xhr
.status
, xhr
: xhr
});
281 evalResponse: function(xhr
) {
282 return this[(this.handleAs
|| 'text') + 'Handler'](xhr
);
285 xmlHandler: function(xhr
) {
286 return xhr
.responseXML
;
289 textHandler: function(xhr
) {
290 return xhr
.responseText
;
293 jsonHandler: function(xhr
) {
294 var r
= xhr
.responseText
;
296 return JSON
.parse(r
);
298 console
.warn('core-ajax caught an exception trying to parse response as JSON:');
299 console
.warn('url:', this.url
);
305 documentHandler: function(xhr
) {
309 blobHandler: function(xhr
) {
313 arraybufferHandler: function(xhr
) {
317 urlChanged: function() {
318 if (!this.handleAs
) {
319 var ext
= String(this.url
).split('.').pop();
322 this.handleAs
= 'json';
329 paramsChanged: function() {
333 bodyChanged: function() {
337 autoChanged: function() {
341 // TODO(sorvell): multiple side-effects could call autoGo
342 // during one micro-task, use a job to have only one action
346 this.goJob
= this.job(this.goJob
, this.go
, 0);
351 * Performs an Ajax request to the specified URL.
356 var args
= this.xhrArgs
|| {};
357 // TODO(sjmiles): we may want XHR to default to POST if body is set
358 args
.body
= this.body
|| args
.body
;
359 args
.params
= this.params
|| args
.params
;
360 if (args
.params
&& typeof(args
.params
) == 'string') {
361 args
.params
= JSON
.parse(args
.params
);
363 args
.headers
= this.headers
|| args
.headers
|| {};
364 if (args
.headers
&& typeof(args
.headers
) == 'string') {
365 args
.headers
= JSON
.parse(args
.headers
);
367 var hasContentType
= Object
.keys(args
.headers
).some(function (header
) {
368 return header
.toLowerCase() === 'content-type';
370 // No Content-Type should be specified if sending `FormData`.
371 // The UA must set the Content-Type w/ a calculated multipart boundary ID.
372 if (args
.body
instanceof FormData
) {
373 delete args
.headers
['Content-Type'];
375 else if (!hasContentType
&& this.contentType
) {
376 args
.headers
['Content-Type'] = this.contentType
;
378 if (this.handleAs
=== 'arraybuffer' || this.handleAs
=== 'blob' ||
379 this.handleAs
=== 'document') {
380 args
.responseType
= this.handleAs
;
382 args
.withCredentials
= this.withCredentials
;
383 args
.callback
= this.receive
.bind(this);
385 args
.method
= this.method
;
387 this.response
= this.error
= this.progress
= null;
388 this.activeRequest
= args
.url
&& this.xhr
.request(args
);
389 if (this.activeRequest
) {
391 var activeRequest
= this.activeRequest
;
392 // IE < 10 doesn't support progress events.
393 if ('onprogress' in activeRequest
) {
394 this.activeRequest
.addEventListener(
397 this.processProgress(progress
, activeRequest
);
398 }.bind(this), false);
401 lengthComputable
: false,
405 return this.activeRequest
;