@lwce/fetch
Advanced tools
Comparing version 0.0.1 to 0.0.2
function substitute(template, variables) { | ||
return template.replace(/\{(.*?)}/g, (_, i) => variables[i]); | ||
return template.replace(/\{(.*?)}/g, (_, i) => encodeURIComponent(variables[i])); | ||
} | ||
function concatPath(parts) { | ||
return parts.map(function (p) { | ||
return p.replace(/(^[\/]*|[\/]*$)/g, ''); | ||
}).join('/'); | ||
} | ||
export class FetchClient { | ||
@@ -19,16 +24,27 @@ constructor(url) { | ||
} | ||
async fetch(input, init, variables) { | ||
composeUrl(input, queryParams, variables) { | ||
// Concat the base URL if not absolute | ||
if (this.url && input.indexOf('://') < 0) { | ||
input = `${this.url}${input}`; | ||
input = concatPath([this.url, input]); | ||
} | ||
// Append the query parameters | ||
if (queryParams) { | ||
Object.keys(queryParams).forEach((q) => { | ||
if (queryParams[q] !== undefined) { | ||
// Could use searchparams with the latest browsers | ||
input += (input.match(/[\?]/g) ? '&' : '?') + encodeURIComponent(q); | ||
if (queryParams[q] !== null) { | ||
input += '=' + encodeURIComponent(queryParams[q]); | ||
} | ||
} | ||
}); | ||
} | ||
// Substitute the variables | ||
if (variables) { | ||
input = substitute(input, variables); | ||
if (init) { | ||
if (typeof init.body === 'string') { | ||
input = substitute(input, variables); | ||
} | ||
} | ||
} | ||
return input; | ||
} | ||
async fetch(input, init, queryParams, variables) { | ||
input = this.composeUrl(input, queryParams, variables); | ||
// Process the request interceptors | ||
@@ -35,0 +51,0 @@ const reqI = this.requestInterceptors; |
@@ -21,3 +21,3 @@ /* | ||
fetch | ||
}, init, url, variables, lazy = false; | ||
}, init, url, queryParams, variables, lazy = false, fetchCurrent = 0; | ||
function update() { | ||
@@ -30,38 +30,49 @@ if (connected) { | ||
} | ||
function fetch(options, v) { | ||
function fetch(options, qp, v) { | ||
if (url) { | ||
const _init = { ...init, ...options }; | ||
pendingResult.loading = true; | ||
const fetchIndex = ++fetchCurrent; | ||
update(); | ||
try { | ||
pendingResult.client.fetch(url, _init, v || variables).then((response) => { | ||
return pendingResult.client.fetch(url, _init, qp || queryParams, v || variables).then((response) => { | ||
return response.json(); | ||
}).then((json) => { | ||
Object.assign(pendingResult, { | ||
loading: false, | ||
data: json, | ||
error: undefined, | ||
initialized: true | ||
}); | ||
update(); | ||
if (fetchIndex == fetchCurrent) { | ||
Object.assign(pendingResult, { | ||
loading: false, | ||
data: json, | ||
error: undefined, | ||
initialized: true | ||
}); | ||
update(); | ||
return Promise.resolve(); | ||
} | ||
}).catch((err) => { | ||
if (fetchIndex == fetchCurrent) { | ||
Object.assign(pendingResult, { | ||
loading: false, | ||
data: undefined, | ||
error: (err && err.toString()) || "Error", | ||
initialized: true | ||
}); | ||
update(); | ||
return Promise.resolve(); | ||
} | ||
}); | ||
} | ||
catch (error) { | ||
if (fetchIndex == fetchCurrent) { | ||
Object.assign(pendingResult, { | ||
loading: false, | ||
data: undefined, | ||
error: (err && err.toString()) || "Error", | ||
error: error.toString(), | ||
initialized: true | ||
}); | ||
update(); | ||
}); | ||
return Promise.resolve(); | ||
} | ||
} | ||
catch (error) { | ||
Object.assign(pendingResult, { | ||
loading: false, | ||
data: undefined, | ||
error: error.toString(), | ||
initialized: true | ||
}); | ||
update(); | ||
} | ||
} | ||
return Promise.reject(); | ||
} | ||
@@ -77,2 +88,3 @@ function handleConfig(options) { | ||
variables = options.variables; | ||
queryParams = options.queryParams; | ||
if (!lazy) { | ||
@@ -79,0 +91,0 @@ fetch(); |
@@ -10,3 +10,4 @@ declare type RequestInterceptor = (init?: RequestInit) => RequestInit | Promise<RequestInit>; | ||
addResponseInterceptor(interceptor: ResponseInterceptor): void; | ||
fetch(input: string, init?: RequestInit, variables?: Record<string, any>): Promise<Response>; | ||
composeUrl(input: string, queryParams?: Record<string, any> | undefined, variables?: Record<string, any> | undefined): string; | ||
fetch(input: string, init?: RequestInit, queryParams?: Record<string, any> | undefined, variables?: Record<string, any> | undefined): Promise<Response>; | ||
} | ||
@@ -13,0 +14,0 @@ export declare function getFetchClient(): any; |
{ | ||
"name": "@lwce/fetch", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"license": "BSD-3-Clause", | ||
"author": "Philippe Riand", | ||
@@ -12,4 +13,5 @@ "bugs": "https://github.com/LWC-Essentials/wire-adapters/issues", | ||
"pack": "yarn build && yarn pack", | ||
"npm-publish": "yarn build && yarn pack && yarn publish --access public", | ||
"clean": "rm ./*.tgz" | ||
"build-publish": "yarn build && yarn pack && yarn publish --access public", | ||
"clean": "rm ./*.tgz", | ||
"test": "jest" | ||
}, | ||
@@ -22,8 +24,12 @@ "files": [ | ||
"@lwc/engine": "1.1.13", | ||
"@lwc/wire-service": "^1.1.13" | ||
"@lwc/wire-service": "^1.1.13", | ||
"@types/jest": "^25.1.4", | ||
"jest": "^25.1.0", | ||
"ts-jest": "^25.2.1", | ||
"typescript": "^3.8.3" | ||
}, | ||
"license": "BSD 3-Clause", | ||
"homepage": "https://github.com/LWC-Essentials/wire-adapters", | ||
"keywords": [ | ||
"lwc", "wire adapter" | ||
"lwc", | ||
"wire adapter" | ||
], | ||
@@ -30,0 +36,0 @@ "engines": { |
@@ -18,12 +18,18 @@ # @lwce/fetch | ||
If this URL is relative, then it is appended to the `FetchClient` base URL (see bellow). | ||
- `init`: the options passed to the fetch method (a [RequestInit](https://fetch.spec.whatwg.org/#requestinit) object). | ||
- `init`: the options passed to the native fetch method (a [RequestInit](https://fetch.spec.whatwg.org/#requestinit) object). | ||
- `queryParams`: query parameters. | ||
The `queryParams` option lists the parameters to be added to the query string of the url. An `undefined` value for a parameter prevents the parameter to be added, while a `null` value adds it without a value (just the parameter name, no '=' sign). Each parameter, name & value, is properly encoded using `encodeURIComponent()`. | ||
- `variables`: substitution variables. | ||
The URL parts between `{}` are substituted by these variables. It also applies to the `body` member of `init`, if any. | ||
The URL parts between `{}` are substituted by these variables. The value is encoded using `encodeURIComponent()`. As it better handles `undefined` and `null` values, it is advised to use `queryParams` for the query string. | ||
Here is an example: | ||
Here is an example showing these capabilities: | ||
```javascript | ||
export default class UserList extends LightningElement { | ||
export default class ComputerList extends LightningElement { | ||
@track variables = { | ||
instance: 'xyz' | ||
} | ||
@track queryParams = { | ||
offset: 0, | ||
@@ -34,14 +40,17 @@ limit: 10 | ||
@wire(useFetch, { | ||
url: '/users?offset={offset}&limit={limit}', | ||
variables: '$variables' | ||
}) users; | ||
``` | ||
url: '/{instance}/computers', | ||
variables: '$variables', | ||
queryParams: '$queryParams' | ||
}) computers; | ||
``` | ||
Note: to make the wire adapter react on a variable value change, the whole `variables` object has to be replaced, as a change in a property is not detected. For example, changing the offset should be done with code like: | ||
The initial fetch URL will be something like: `https://myserver/api/xyz?offset=0&limit=10` | ||
Note: to make the wire adapter react on a variable value change, the whole `queryParams` or `variables` objects have to be replaced, as a change in one of their sub-properties is not detected. For example, changing the offset should be done with code like: | ||
```javascript | ||
// Right way to update an object | ||
handleFirst() { | ||
this.variables = { | ||
...this.variables, | ||
this.queryParams = { | ||
...this.queryParams, | ||
offset: 0 | ||
@@ -53,23 +62,20 @@ } | ||
// handleFirst() { | ||
// this.variables.offset = 0; | ||
// this.queryParams.offset = 0; | ||
// } | ||
``` | ||
### Fetch Result | ||
The fetch wire adapter assigns the following values to the variable: | ||
The fetch wire adapter assigns the following values to the result variable: | ||
```javascript | ||
loading: boolean; // true when the request is excuting | ||
data?: any; // Result if the request was succesful | ||
error?: string; // Error message if an error occurred | ||
initialized: boolean; // true of the request has been executed at least once | ||
loading: boolean; // true when the request is being executed | ||
data?: any; // Data if the request was succesful | ||
error?: string; // Error message if an error occurred | ||
initialized: boolean; // true when the request has already been executed at least once | ||
client: FetchClient, // FetchClient used for the request | ||
fetch?: (options, v) // fetch() method to re-execute the request (see 'lazy') | ||
``` | ||
client: FetchClient, // FetchClient used for the request | ||
fetch?: (options, q, v): Promise<void> // fetch() method to re-execute the request (see 'lazy') | ||
``` | ||
### FetchClient | ||
@@ -84,5 +90,4 @@ | ||
}) mydata; | ||
``` | ||
``` | ||
### Fetch URL | ||
@@ -122,13 +127,19 @@ | ||
The request is automatically emitted when the wire adapter config is available, which means when the component is connected to the DOM. This works well for data needed by the component to display right away but, sometimes, the request should be executed manually. This is certainly true for update requests (POST,PUT,DELETE) and for data requested on demand (list for a pop-up, ...). To support that, the wire adapter offers a 'lazy' mode. When set to true, the request is only executed with an explicit call the `fetch()`. | ||
The request is automatically emitted when the wire adapter configuration is available, which means when the component is connected to the DOM. This works well for data needed by the component to display right away but, sometimes, the request should be executed manually. This is certainly true for update requests (POST,PUT,DELETE) and for data requested on demand (list for a pop-up, ...). | ||
The fetch method has the following signature: | ||
To support that, the wire adapter offers a `lazy` mode. When set to true, the request is only executed with an explicit call to a `fetch()` method, provided as part of the @wire variable. | ||
This fetch method has the following signature: | ||
```javascript | ||
fetch(init?: RequestInit, variables?: Record<string, any>): void | ||
fetch(init?: RequestInit, queryParams?: Record<string, any>, variables?: Record<string, any>): Promise<void> | ||
``` | ||
- init | ||
- `init` | ||
These are initialization parameters that are merged with the ones defined at the adapter level. | ||
- variables | ||
Replaces (no merge) the variables defined at the wire adapter level. | ||
- `queryParams` | ||
Replaces, if defined, the query parameters defined at the wire adapter level. | ||
- `variables` | ||
Replaces, if defined, the variables defined at the wire adapter level. | ||
The function returns a `Promise` that can be observed to know when the result has been retrieved. The `Promise` does not provide that value, but it can be accessed from the @wire variable. | ||
@@ -145,3 +156,5 @@ Here is an example: | ||
// Explicitly fetch the data | ||
this.mydata.fetch() | ||
this.mydata.fetch().then( () => { | ||
console.log('The data was retrieved: '+this.mydata.data); | ||
} | ||
} | ||
@@ -153,5 +166,11 @@ ``` | ||
`useFetch` does not use a store behind the scene, which means that the request results are not cached and shared between adapters. Such a cache should better be implemented at the `FetchClient` level. | ||
`useFetch` is kept simple on purpose, but should be enhanced over time: | ||
- It does not use a store behind the scene, which means that the request results are not cached and shared between adapters. | ||
- It does not normalize the data (see: [normalizr.js](https://github.com/paularmstrong/normalizr)). | ||
The target browser should have native Fetch available, see: [CanIUse Fetch](https://caniuse.com/#feat=fetch). If not, (ex: IE11), there are some polyfills available: [Fetch Polyfill](https://github.github.io/fetch/). | ||
## FetchClient | ||
@@ -158,0 +177,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
23315
224
186
6