| <!-- |
| Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
| The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
| Code distributed by Google as part of the polymer project is also |
| subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
| --> |
| |
| <link rel="import" href="../polymer/polymer.html"> |
| <link rel="import" href="iron-request.html"> |
| |
| <!-- |
| The `iron-ajax` element exposes network request functionality. |
| |
| <iron-ajax |
| auto |
| url="http://gdata.youtube.com/feeds/api/videos/" |
| params='{"alt":"json", "q":"chrome"}' |
| handle-as="json" |
| on-response="handleResponse" |
| debounce-duration="300"></iron-ajax> |
| |
| With `auto` set to `true`, the element performs a request whenever |
| its `url`, `params` or `body` properties are changed. Automatically generated |
| requests will be debounced in the case that multiple attributes are changed |
| sequentially. |
| |
| Note: The `params` attribute must be double quoted JSON. |
| |
| You can trigger a request explicitly by calling `generateRequest` on the |
| element. |
| |
| @demo demo/index.html |
| @hero hero.svg |
| --> |
| |
| <script> |
| 'use strict'; |
| |
| Polymer({ |
| |
| is: 'iron-ajax', |
| |
| /** |
| * Fired when a request is sent. |
| * |
| * @event request |
| */ |
| |
| /** |
| * Fired when a response is received. |
| * |
| * @event response |
| */ |
| |
| /** |
| * Fired when an error is received. |
| * |
| * @event error |
| */ |
| |
| hostAttributes: { |
| hidden: true |
| }, |
| |
| properties: { |
| /** |
| * The URL target of the request. |
| */ |
| url: { |
| type: String |
| }, |
| |
| /** |
| * An object that contains query parameters to be appended to the |
| * specified `url` when generating a request. If you wish to set the body |
| * content when making a POST request, you should use the `body` property |
| * instead. |
| */ |
| params: { |
| type: Object, |
| value: function() { |
| return {}; |
| } |
| }, |
| |
| /** |
| * The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'. |
| * Default is 'GET'. |
| */ |
| method: { |
| type: String, |
| value: 'GET' |
| }, |
| |
| /** |
| * HTTP request headers to send. |
| * |
| * Example: |
| * |
| * <iron-ajax |
| * auto |
| * url="http://somesite.com" |
| * headers='{"X-Requested-With": "XMLHttpRequest"}' |
| * handle-as="json"></iron-ajax> |
| * |
| * Note: setting a `Content-Type` header here will override the value |
| * specified by the `contentType` property of this element. |
| */ |
| headers: { |
| type: Object, |
| value: function() { |
| return {}; |
| } |
| }, |
| |
| /** |
| * Content type to use when sending data. If the `contentType` property |
| * is set and a `Content-Type` header is specified in the `headers` |
| * property, the `headers` property value will take precedence. |
| */ |
| contentType: { |
| type: String, |
| value: null |
| }, |
| |
| /** |
| * Body content to send with the request, typically used with "POST" |
| * requests. |
| * |
| * If body is a string it will be sent unmodified. |
| * |
| * If Content-Type is set to a value listed below, then |
| * the body will be encoded accordingly. |
| * |
| * * `content-type="application/json"` |
| * * body is encoded like `{"foo":"bar baz","x":1}` |
| * * `content-type="application/x-www-form-urlencoded"` |
| * * body is encoded like `foo=bar+baz&x=1` |
| * |
| * Otherwise the body will be passed to the browser unmodified, and it |
| * will handle any encoding (e.g. for FormData, Blob, ArrayBuffer). |
| * |
| * @type (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object) |
| */ |
| body: { |
| type: Object, |
| value: null |
| }, |
| |
| /** |
| * Toggle whether XHR is synchronous or asynchronous. Don't change this |
| * to true unless You Know What You Are Doing™. |
| */ |
| sync: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * Specifies what data to store in the `response` property, and |
| * to deliver as `event.detail.response` in `response` events. |
| * |
| * One of: |
| * |
| * `text`: uses `XHR.responseText`. |
| * |
| * `xml`: uses `XHR.responseXML`. |
| * |
| * `json`: uses `XHR.responseText` parsed as JSON. |
| * |
| * `arraybuffer`: uses `XHR.response`. |
| * |
| * `blob`: uses `XHR.response`. |
| * |
| * `document`: uses `XHR.response`. |
| */ |
| handleAs: { |
| type: String, |
| value: 'json' |
| }, |
| |
| /** |
| * Set the withCredentials flag on the request. |
| */ |
| withCredentials: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * Set the timeout flag on the request. |
| */ |
| timeout: { |
| type: Number, |
| value: 0 |
| }, |
| |
| /** |
| * If true, automatically performs an Ajax request when either `url` or |
| * `params` changes. |
| */ |
| auto: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * If true, error messages will automatically be logged to the console. |
| */ |
| verbose: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * The most recent request made by this iron-ajax element. |
| */ |
| lastRequest: { |
| type: Object, |
| notify: true, |
| readOnly: true |
| }, |
| |
| /** |
| * True while lastRequest is in flight. |
| */ |
| loading: { |
| type: Boolean, |
| notify: true, |
| readOnly: true |
| }, |
| |
| /** |
| * lastRequest's response. |
| * |
| * Note that lastResponse and lastError are set when lastRequest finishes, |
| * so if loading is true, then lastResponse and lastError will correspond |
| * to the result of the previous request. |
| * |
| * The type of the response is determined by the value of `handleAs` at |
| * the time that the request was generated. |
| * |
| * @type {Object} |
| */ |
| lastResponse: { |
| type: Object, |
| notify: true, |
| readOnly: true |
| }, |
| |
| /** |
| * lastRequest's error, if any. |
| * |
| * @type {Object} |
| */ |
| lastError: { |
| type: Object, |
| notify: true, |
| readOnly: true |
| }, |
| |
| /** |
| * An Array of all in-flight requests originating from this iron-ajax |
| * element. |
| */ |
| activeRequests: { |
| type: Array, |
| notify: true, |
| readOnly: true, |
| value: function() { |
| return []; |
| } |
| }, |
| |
| /** |
| * Length of time in milliseconds to debounce multiple automatically generated requests. |
| */ |
| debounceDuration: { |
| type: Number, |
| value: 0, |
| notify: true |
| }, |
| |
| /** |
| * Prefix to be stripped from a JSON response before parsing it. |
| * |
| * In order to prevent an attack using CSRF with Array responses |
| * (http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx/) |
| * many backends will mitigate this by prefixing all JSON response bodies |
| * with a string that would be nonsensical to a JavaScript parser. |
| * |
| */ |
| jsonPrefix: { |
| type: String, |
| value: '' |
| }, |
| |
| _boundHandleResponse: { |
| type: Function, |
| value: function() { |
| return this._handleResponse.bind(this); |
| } |
| } |
| }, |
| |
| observers: [ |
| '_requestOptionsChanged(url, method, params.*, headers, contentType, ' + |
| 'body, sync, handleAs, jsonPrefix, withCredentials, timeout, auto)' |
| ], |
| |
| /** |
| * The query string that should be appended to the `url`, serialized from |
| * the current value of `params`. |
| * |
| * @return {string} |
| */ |
| get queryString () { |
| var queryParts = []; |
| var param; |
| var value; |
| |
| for (param in this.params) { |
| value = this.params[param]; |
| param = window.encodeURIComponent(param); |
| |
| if (Array.isArray(value)) { |
| for (var i = 0; i < value.length; i++) { |
| queryParts.push(param + '=' + window.encodeURIComponent(value[i])); |
| } |
| } else if (value !== null) { |
| queryParts.push(param + '=' + window.encodeURIComponent(value)); |
| } else { |
| queryParts.push(param); |
| } |
| } |
| |
| return queryParts.join('&'); |
| }, |
| |
| /** |
| * The `url` with query string (if `params` are specified), suitable for |
| * providing to an `iron-request` instance. |
| * |
| * @return {string} |
| */ |
| get requestUrl() { |
| var queryString = this.queryString; |
| |
| if (queryString) { |
| var bindingChar = this.url.indexOf('?') >= 0 ? '&' : '?'; |
| return this.url + bindingChar + queryString; |
| } |
| |
| return this.url; |
| }, |
| |
| /** |
| * An object that maps header names to header values, first applying the |
| * the value of `Content-Type` and then overlaying the headers specified |
| * in the `headers` property. |
| * |
| * @return {Object} |
| */ |
| get requestHeaders() { |
| var headers = {}; |
| var contentType = this.contentType; |
| if (contentType == null && (typeof this.body === 'string')) { |
| contentType = 'application/x-www-form-urlencoded'; |
| } |
| if (contentType) { |
| headers['content-type'] = contentType; |
| } |
| var header; |
| |
| if (this.headers instanceof Object) { |
| for (header in this.headers) { |
| headers[header] = this.headers[header].toString(); |
| } |
| } |
| |
| return headers; |
| }, |
| |
| /** |
| * Request options suitable for generating an `iron-request` instance based |
| * on the current state of the `iron-ajax` instance's properties. |
| * |
| * @return {{ |
| * url: string, |
| * method: (string|undefined), |
| * async: (boolean|undefined), |
| * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|undefined|Object), |
| * headers: (Object|undefined), |
| * handleAs: (string|undefined), |
| * jsonPrefix: (string|undefined), |
| * withCredentials: (boolean|undefined)}} |
| */ |
| toRequestOptions: function() { |
| return { |
| url: this.requestUrl || '', |
| method: this.method, |
| headers: this.requestHeaders, |
| body: this.body, |
| async: !this.sync, |
| handleAs: this.handleAs, |
| jsonPrefix: this.jsonPrefix, |
| withCredentials: this.withCredentials, |
| timeout: this.timeout |
| }; |
| }, |
| |
| /** |
| * Performs an AJAX request to the specified URL. |
| * |
| * @return {!IronRequestElement} |
| */ |
| generateRequest: function() { |
| var request = /** @type {!IronRequestElement} */ (document.createElement('iron-request')); |
| var requestOptions = this.toRequestOptions(); |
| |
| this.activeRequests.push(request); |
| |
| request.completes.then( |
| this._boundHandleResponse |
| ).catch( |
| this._handleError.bind(this, request) |
| ).then( |
| this._discardRequest.bind(this, request) |
| ); |
| |
| request.send(requestOptions); |
| |
| this._setLastRequest(request); |
| this._setLoading(true); |
| |
| this.fire('request', { |
| request: request, |
| options: requestOptions |
| }, {bubbles: false}); |
| |
| return request; |
| }, |
| |
| _handleResponse: function(request) { |
| if (request === this.lastRequest) { |
| this._setLastResponse(request.response); |
| this._setLastError(null); |
| this._setLoading(false); |
| } |
| this.fire('response', request, {bubbles: false}); |
| }, |
| |
| _handleError: function(request, error) { |
| if (this.verbose) { |
| console.error(error); |
| } |
| |
| if (request === this.lastRequest) { |
| this._setLastError({ |
| request: request, |
| error: error |
| }); |
| this._setLastResponse(null); |
| this._setLoading(false); |
| } |
| this.fire('error', { |
| request: request, |
| error: error |
| }, {bubbles: false}); |
| }, |
| |
| _discardRequest: function(request) { |
| var requestIndex = this.activeRequests.indexOf(request); |
| |
| if (requestIndex > -1) { |
| this.activeRequests.splice(requestIndex, 1); |
| } |
| }, |
| |
| _requestOptionsChanged: function() { |
| this.debounce('generate-request', function() { |
| if (this.url == null) { |
| return; |
| } |
| |
| if (this.auto) { |
| this.generateRequest(); |
| } |
| }, this.debounceDuration); |
| }, |
| |
| }); |
| </script> |