isu-elements
Version:
Polymer components for building web apps.
269 lines (245 loc) • 6.48 kB
JavaScript
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)