UNPKG

isu-elements

Version:

Polymer components for building web apps.

269 lines (245 loc) 6.48 kB
import { PolymerElement } from '@polymer/polymer' import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class' import { BaseBehavior } from './behaviors/base-behavior.js' /** * ### Usage 1: * * ```html * <isu-fetch request="{{request}}" response="{{response}}" error="{{error}}"></isu-fetch> * * ``` * ``` * request = { * url: "/path/to/index.do", * method: "POST", * headers: { * "Cache-Control": "no-cache" * } *} * ``` * ### Usage 2: * * ``` * const request = { * url: "/path/to/index.do", * method: "POST", * headers: { * "Cache-Control": "no-cache" * } *}; * * new IsuFetch().fetchIt(request) * .then(res => res.json()) * .then(console.log) * .catch(console.log); * ``` * ### Also, you can mock the responses of your http requests, by following belows: * #### 1. Import mock_setup.js before the importing of polymer libs. * * ```html * <head> * ... * <script type="text/javascript" src="bower_components/isu-elements/utils/mock_setup.js"></script> * * polymer libs import after here * </head> * * ``` * #### 2. Create mockData.js * For Example: * ``` * import {data} from "./data.js"; * * MockDataPool.when("POST", "/path/to/index.do") * .withExpectedHeader("content-type", "application/json;charset=utf-8") * .withExpectedHeader("Cache-Control", "no-cache") * .responseWith({status: 200, body: JSON.stringify(data)}); * ``` * * #### 3. Append a searchParam on your request url to open mock mode. * * For example ``http://127.0.0.1:8000/components/isu-elements/demo/isu-fetch/index.html?mock=/your/path/to/mockData.js``, * * ``/your/path/to/`` is a relative path to index.html. For example, if index.html and mockData.js are located under a same * folder, it can be ``index.html?mock=mockData.js`` or ``index.html?mock=./mockData.js`` * * @customElement * @polymer * @demo demo/isu-fetch/index.html */ export class IsuFetch extends mixinBehaviors([BaseBehavior], PolymerElement) { static get properties () { return { /** * See [Request API](https://developer.mozilla.org/en-US/docs/Web/API/Request) * @type {Object} */ request: { type: Object }, /** * Error is undefined when window.__mockEnabled is true * @type {{content:*}} */ error: { type: Object, notify: true }, /** * See [Response API](https://developer.mozilla.org/en-US/docs/Web/API/Response) */ response: { type: Object, notify: true }, /** * How to handle the response body, the handled result will access as the property 'responseBody'. * json/text/blob/arrayBuffer * @type {string} * @default 'json' */ handleResponseAs: { type: String, value: 'json' }, /** * Handled response body. * @type {{content:*}} */ responseBody: { type: Object, notify: true }, __defaultRequest: { type: Object, value: { credentials: 'include' }, readOnly: true }, __defaultHeaders: { type: Object, value: function () { return { 'content-type': 'application/json;charset=utf-8' } }, readOnly: true }, __controller: { type: Object }, __signal: { type: Object }, loading: { type: Boolean, value: false } } } static get is () { return 'isu-fetch' } static get observers () { return [ '__requestChange(request)', '__responseChange(response)' ] } constructor () { super() if ('AbortController' in window) { // todo: abortControllers persist in global scope this.__controller = new AbortController() this.__signal = this.__controller.signal } } __getCorrectedRequest (request) { if (request.method === 'GET') { request.url = request.url + this.__formatUrlData(request.url, JSON.parse((request.body || '{}'))) delete request.body } const req = Request.prototype.isPrototypeOf(request) ? request : new Request(request.url, request) // TODO set default value to req return req } __formatUrlData (url, data) { let paramStr = '' const dataKeys = Object.keys(data) const dataValues = Object.values(data) if (dataKeys.length > 0) { paramStr += url.indexOf('?') === -1 ? '?' : '' dataKeys.forEach((key, index) => { paramStr += `&${key}=${dataValues[index]}` }) } return paramStr } __requestChange (request) { if (!request) return return this.fetchIt(request) } __responseChange (response) { const resClone = response.clone() if (response.ok) { const handleAs = this.handleResponseAs || 'text' if (response.headers.has('Content-length') && response.headers.get('Content-length') == 0) { this.responseBody = {} return } resClone[handleAs]().then(data => this.responseBody = { content: data }) } else { resClone.text().then(err => this.error = { content: err }) } } /** * Fetch you request, if window.__mockEnabled == true, you can get your mock response. * @param {Request|Object} request * @param option * @return {Promise} */ fetchIt (request, option = { loading: this.loading }) { const collectedReq = this.__getCorrectedRequest(request) if (window.__mockEnabled && typeof MockDataPool !== 'undefined') { const matchedRes = MockDataPool.match(collectedReq) if (matchedRes) { this.response = new Response(matchedRes.body, matchedRes) return Promise.resolve(this.response) } } option.loading && this.showLoading() return window.fetch(collectedReq, { signal: this.__signal }) .then(res => { this.response = res return res }) .catch(err => { this.error = { content: err } return Promise.reject(err) }) .finally(() => { option.loading && this.hideLoading() }) } /** * Abort your request. */ abort () { this.__controller && this.__controller.abort() } /** * Abort all pending requests. */ abortAll () { // todo } /** * @param reqArr */ fetchAll (reqArr = []) { // todo } } window.customElements.define(IsuFetch.is, IsuFetch)