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