| OLD | NEW |
| (Empty) |
| 1 <!-- | |
| 2 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
| 3 This code may only be used under the BSD style license found at http://polymer.g
ithub.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/CONTRI
BUTORS.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/PATEN
TS.txt | |
| 8 --> | |
| 9 | |
| 10 <link rel="import" href="../polymer/polymer.html"> | |
| 11 <link rel="import" href="../promise-polyfill/promise-polyfill-lite.html"> | |
| 12 | |
| 13 <!-- | |
| 14 iron-request can be used to perform XMLHttpRequests. | |
| 15 | |
| 16 <iron-request id="xhr"></iron-request> | |
| 17 ... | |
| 18 this.$.xhr.send({url: url, params: params}); | |
| 19 --> | |
| 20 <script> | |
| 21 Polymer({ | |
| 22 is: 'iron-request', | |
| 23 | |
| 24 properties: { | |
| 25 | |
| 26 /** | |
| 27 * A reference to the XMLHttpRequest instance used to generate the | |
| 28 * network request. | |
| 29 * | |
| 30 * @attribute xhr | |
| 31 * @type XMLHttpRequest | |
| 32 * @default `new XMLHttpRequest` | |
| 33 */ | |
| 34 xhr: { | |
| 35 type: Object, | |
| 36 notify: true, | |
| 37 readOnly: true, | |
| 38 value: function() { | |
| 39 return new XMLHttpRequest(); | |
| 40 } | |
| 41 }, | |
| 42 | |
| 43 /** | |
| 44 * A reference to the parsed response body, if the `xhr` has completely | |
| 45 * resolved. | |
| 46 * | |
| 47 * @attribute response | |
| 48 * @type {*} | |
| 49 * @default null | |
| 50 */ | |
| 51 response: { | |
| 52 type: Object, | |
| 53 notify: true, | |
| 54 readOnly: true, | |
| 55 value: function() { | |
| 56 return null; | |
| 57 } | |
| 58 }, | |
| 59 | |
| 60 /** | |
| 61 * A promise that resolves when the `xhr` response comes back, or rejects | |
| 62 * if there is an error before the `xhr` completes. | |
| 63 * | |
| 64 * @attribute completes | |
| 65 * @type Promise | |
| 66 * @default `new Promise` | |
| 67 */ | |
| 68 completes: { | |
| 69 type: Object, | |
| 70 readOnly: true, | |
| 71 notify: true, | |
| 72 value: function() { | |
| 73 return new Promise(function (resolve, reject) { | |
| 74 this.resolveCompletes = resolve; | |
| 75 this.rejectCompletes = reject; | |
| 76 }.bind(this)); | |
| 77 } | |
| 78 }, | |
| 79 | |
| 80 /** | |
| 81 * An object that contains progress information emitted by the XHR if | |
| 82 * available. | |
| 83 * | |
| 84 * @attribute progress | |
| 85 * @type Object | |
| 86 * @default {} | |
| 87 */ | |
| 88 progress: { | |
| 89 type: Object, | |
| 90 notify: true, | |
| 91 readOnly: true, | |
| 92 value: function() { | |
| 93 return {}; | |
| 94 } | |
| 95 }, | |
| 96 | |
| 97 /** | |
| 98 * Aborted will be true if an abort of the request is attempted. | |
| 99 * | |
| 100 * @attribute aborted | |
| 101 * @type boolean | |
| 102 * @default false | |
| 103 */ | |
| 104 aborted: { | |
| 105 type: Boolean, | |
| 106 notify: true, | |
| 107 readOnly: true, | |
| 108 value: false, | |
| 109 } | |
| 110 }, | |
| 111 | |
| 112 /** | |
| 113 * Succeeded is true if the request succeeded. The request succeeded if the | |
| 114 * status code is greater-than-or-equal-to 200, and less-than 300. Also, | |
| 115 * the status code 0 is accepted as a success even though the outcome may | |
| 116 * be ambiguous. | |
| 117 * | |
| 118 * @return {boolean} | |
| 119 */ | |
| 120 get succeeded() { | |
| 121 var status = this.xhr.status || 0; | |
| 122 | |
| 123 // Note: if we are using the file:// protocol, the status code will be 0 | |
| 124 // for all outcomes (successful or otherwise). | |
| 125 return status === 0 || | |
| 126 (status >= 200 && status < 300); | |
| 127 }, | |
| 128 | |
| 129 /** | |
| 130 * Sends an HTTP request to the server and returns the XHR object. | |
| 131 * | |
| 132 * @param {{ | |
| 133 * url: string, | |
| 134 * method: (string|undefined), | |
| 135 * async: (boolean|undefined), | |
| 136 * body: (ArrayBuffer|ArrayBufferView|Blob|Document|FormData|null|string|u
ndefined), | |
| 137 * headers: (Object|undefined), | |
| 138 * handleAs: (string|undefined), | |
| 139 * withCredentials: (boolean|undefined)}} options - | |
| 140 * url The url to which the request is sent. | |
| 141 * method The HTTP method to use, default is GET. | |
| 142 * async By default, all requests are sent asynchronously. To send synch
ronous requests, | |
| 143 * set to true. | |
| 144 * body The content for the request body for POST method. | |
| 145 * headers HTTP request headers. | |
| 146 * handleAs The response type. Default is 'text'. | |
| 147 * withCredentials Whether or not to send credentials on the request. De
fault is false. | |
| 148 * @return {Promise} | |
| 149 */ | |
| 150 send: function (options) { | |
| 151 var xhr = this.xhr; | |
| 152 | |
| 153 if (xhr.readyState > 0) { | |
| 154 return null; | |
| 155 } | |
| 156 | |
| 157 xhr.addEventListener('readystatechange', function () { | |
| 158 if (xhr.readyState === 4 && !this.aborted) { | |
| 159 | |
| 160 if (!this.succeeded) { | |
| 161 this.rejectCompletes(new Error('The request failed with status code:
' + this.xhr.status)); | |
| 162 return; | |
| 163 } | |
| 164 | |
| 165 this._setResponse(this.parseResponse()); | |
| 166 this.resolveCompletes(this); | |
| 167 } | |
| 168 }.bind(this)); | |
| 169 | |
| 170 xhr.addEventListener('progress', function (progress) { | |
| 171 this._setProgress({ | |
| 172 lengthComputable: progress.lengthComputable, | |
| 173 loaded: progress.loaded, | |
| 174 total: progress.total | |
| 175 }); | |
| 176 }.bind(this)) | |
| 177 | |
| 178 xhr.addEventListener('error', function (error) { | |
| 179 this.rejectCompletes(error); | |
| 180 }.bind(this)); | |
| 181 | |
| 182 xhr.addEventListener('abort', function () { | |
| 183 this.rejectCompletes(new Error('Request aborted.')); | |
| 184 }.bind(this)); | |
| 185 | |
| 186 xhr.open( | |
| 187 options.method || 'GET', | |
| 188 options.url, | |
| 189 options.async !== false | |
| 190 ); | |
| 191 | |
| 192 if (options.headers) { | |
| 193 Object.keys(options.headers).forEach(function (requestHeader) { | |
| 194 xhr.setRequestHeader( | |
| 195 requestHeader, | |
| 196 options.headers[requestHeader] | |
| 197 ); | |
| 198 }, this); | |
| 199 } | |
| 200 | |
| 201 // In IE, `xhr.responseType` is an empty string when the response | |
| 202 // returns. Hence, caching it as `xhr._responseType`. | |
| 203 xhr.responseType = xhr._responseType = (options.handleAs || 'text'); | |
| 204 xhr.withCredentials = !!options.withCredentials; | |
| 205 | |
| 206 xhr.send(options.body); | |
| 207 | |
| 208 return this.completes; | |
| 209 }, | |
| 210 | |
| 211 /** | |
| 212 * Attempts to parse the response body of the XHR. If parsing succeeds, | |
| 213 * the value returned will be deserialized based on the `responseType` | |
| 214 * set on the XHR. | |
| 215 * | |
| 216 * @return {*} The parsed response, | |
| 217 * or undefined if there was an empty response or parsing failed. | |
| 218 */ | |
| 219 parseResponse: function () { | |
| 220 var xhr = this.xhr; | |
| 221 var responseType = this.xhr.responseType || | |
| 222 this.xhr._responseType; | |
| 223 // If we don't have a natural `xhr.responseType`, we prefer parsing | |
| 224 // `xhr.responseText` over returning `xhr.response`.. | |
| 225 var preferResponseText = !this.xhr.responseType; | |
| 226 | |
| 227 try { | |
| 228 switch (responseType) { | |
| 229 case 'json': | |
| 230 // If xhr.response is undefined, responseType `json` may | |
| 231 // not be supported. | |
| 232 if (preferResponseText || xhr.response === undefined) { | |
| 233 // If accessing `xhr.responseText` throws, responseType `json` | |
| 234 // is supported and the result is rightly `undefined`. | |
| 235 try { | |
| 236 xhr.responseText; | |
| 237 } catch (e) { | |
| 238 return xhr.response; | |
| 239 } | |
| 240 | |
| 241 // Otherwise, attempt to parse `xhr.responseText` as JSON. | |
| 242 if (xhr.responseText) { | |
| 243 return JSON.parse(xhr.responseText); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 return xhr.response; | |
| 248 case 'xml': | |
| 249 return xhr.responseXML; | |
| 250 case 'blob': | |
| 251 case 'document': | |
| 252 case 'arraybuffer': | |
| 253 return xhr.response; | |
| 254 case 'text': | |
| 255 default: | |
| 256 return xhr.responseText; | |
| 257 } | |
| 258 } catch (e) { | |
| 259 this.rejectCompletes(new Error('Could not parse response. ' + e.message)
); | |
| 260 } | |
| 261 }, | |
| 262 | |
| 263 /** | |
| 264 * Aborts the request. | |
| 265 */ | |
| 266 abort: function () { | |
| 267 this._setAborted(true); | |
| 268 this.xhr.abort(); | |
| 269 } | |
| 270 }); | |
| 271 </script> | |
| 272 | |
| OLD | NEW |