improve docs for m.request
This commit is contained in:
parent
36e6894859
commit
7fa3fee9ce
1 changed files with 90 additions and 15 deletions
105
docs/request.md
105
docs/request.md
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
- [API](#api)
|
||||
- [How it works](#how-it-works)
|
||||
- [Typical workflow](#typical-workflow)
|
||||
- [Dynamic URLs](#dynamic-urls)
|
||||
- [Why JSON instead of HTML](#why-json-instead-of-html)
|
||||
- [Why XMLHttpRequest instead of fetch](#why-xmlhttprequest-instead-of-fetch)
|
||||
|
|
@ -13,21 +14,22 @@
|
|||
|
||||
`stream = m.request(options)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
--------------------- | --------------------------------- | -------- | ---
|
||||
`options.method` | `String` | Yes | The HTTP method to use. This value should be one of the following: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD` or `OPTIONS`.
|
||||
`options.url` | `String` | Yes | The URL to send the request to. The URL may be either absolute or relative, and it may contain [interpolations](#dynamic-urls).
|
||||
`options.data` | `any` | No | The data to be interpolated into the URL and serialized into the querystring (for GET requests) or body (for other types of requests).
|
||||
`options.async` | `Boolean` | No | Whether the request should be asynchronous. Defaults to `true`.
|
||||
`options.user` | `String` | No | A username for HTTP authorization. Defaults to `undefined`.
|
||||
`options.password` | `String` | No | A password for HTTP authorization. Defaults to `undefined`. This option is provided for `XMLHttpRequest` compatibility, but you should avoid using it because it sends the password in plain text over the network.
|
||||
`options.config` | `xhr = Function(xhr)` | No | Exposes the underlying XMLHttpRequest object for low-level configuration. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||
`options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`.
|
||||
`options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the response. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses.
|
||||
`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`
|
||||
`options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods.
|
||||
**returns** | `Stream` | | A stream that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods
|
||||
Argument | Type | Required | Description
|
||||
---------------------- | --------------------------------- | -------- | ---
|
||||
`options.method` | `String` | Yes | The HTTP method to use. This value should be one of the following: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD` or `OPTIONS`.
|
||||
`options.url` | `String` | Yes | The URL to send the request to. The URL may be either absolute or relative, and it may contain [interpolations](#dynamic-urls).
|
||||
`options.data` | `any` | No | The data to be interpolated into the URL and serialized into the querystring (for GET requests) or body (for other types of requests).
|
||||
`options.async` | `Boolean` | No | Whether the request should be asynchronous. Defaults to `true`.
|
||||
`options.user` | `String` | No | A username for HTTP authorization. Defaults to `undefined`.
|
||||
`options.password` | `String` | No | A password for HTTP authorization. Defaults to `undefined`. This option is provided for `XMLHttpRequest` compatibility, but you should avoid using it because it sends the password in plain text over the network.
|
||||
`options.config` | `xhr = Function(xhr)` | No | Exposes the underlying XMLHttpRequest object for low-level configuration. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||
`options.type` | `any = Function(any)` | No | A constructor to be applied to each object in the response. Defaults to the [identity function](https://en.wikipedia.org/wiki/Identity_function).
|
||||
`options.serialize` | `string = Function(any)` | No | A serialization method to be applied to `data`. Defaults to `JSON.stringify`.
|
||||
`options.deserialize` | `any = Function(string)` | No | A deserialization method to be applied to the response. Defaults to a small wrapper around `JSON.parse` that returns `null` for empty responses.
|
||||
`options.extract` | `string = Function(xhr, options)` | No | A hook to specify how the XMLHttpRequest response should be read. Useful for reading response headers and cookies. Defaults to a function that returns `xhr.responseText`
|
||||
`options.initialValue` | `any` | No | A value to populate the returned stream before the request completes
|
||||
`options.useBody` | `Boolean` | No | Force the use of the HTTP body section for `data` in `GET` requests when set to `true`, or the use of querystring for other HTTP methods when set to `false`. Defaults to `false` for `GET` requests and `true` for other methods.
|
||||
**returns** | `Stream` | | A stream that resolves to the response data, after it has been piped through the `extract`, `deserialize` and `type` methods
|
||||
|
||||
[How to read signatures](signatures.md)
|
||||
|
||||
|
|
@ -48,6 +50,79 @@ m.request({
|
|||
|
||||
Calls to `m.request` return a [stream](prop.md).
|
||||
|
||||
---
|
||||
|
||||
### Typical workflow
|
||||
|
||||
Here's an illustrative example of a self-contained component that uses `m.request` to retrieve some data from a server.
|
||||
|
||||
```javascript
|
||||
var SimpleExample = {
|
||||
oninit: function(vnode) {
|
||||
vnode.state.items = m.request({
|
||||
method: "GET",
|
||||
url: "/api/items",
|
||||
initialValue: []
|
||||
})
|
||||
},
|
||||
view: function(vnode) {
|
||||
return vnode.state.items().map(function(item) {
|
||||
return m("div", item.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/", {
|
||||
"/": SimpleExample
|
||||
})
|
||||
```
|
||||
|
||||
Let's assume making a request to the server URL `/api/items` returns an array of objects in JSON format.
|
||||
|
||||
When `m.route` is called at the bottom, `MyComponent` is initialized. `oninit` is called, which calls `m.request` and assigns its return value (a stream) to `vnode.state.items`. This stream contains the `initialValue` (i.e. an empty array), and this value can be retrieved by calling the stream as a function (i.e. `value = vnode.state.items()`). After the oninit method returns, the component is then rendered. Since `vnode.state.items()` returns an empty array, the component's `view` method also returns an empty array, so no DOM elements are created. When the request to the server completes, `m.request` parses the response data into a Javascript array of objects and sets the value of the stream to that array. Then, the component is rendered again. This time, `vnode.state.items()` returns a non-empty array, so the component's `view` method returns an array of vnodes, which in turn are rendered into `div` DOM elements.
|
||||
|
||||
Here's an expanded version of the example above that implements a loading indicator and an error message:
|
||||
|
||||
```javascript
|
||||
var RobustExample = {
|
||||
oninit: function(vnode) {
|
||||
var req = m.request({
|
||||
method: "GET",
|
||||
url: "/api/items",
|
||||
})
|
||||
vnode.state.items = req.catch(function() {
|
||||
return []
|
||||
})
|
||||
vnode.state.error = req.error.map(this.errorView)
|
||||
},
|
||||
view: function(vnode) {
|
||||
return [
|
||||
vnode.state.items() ? vnode.state.items().map(function(item) {
|
||||
return m("div", item.name)
|
||||
}) : m(".loading-icon"),
|
||||
vnode.state.error(),
|
||||
]
|
||||
},
|
||||
errorView: function(e) {
|
||||
return m(".error", "An error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
m.route(document.body, "/", {
|
||||
"/": MyComponent
|
||||
})
|
||||
```
|
||||
|
||||
When this component is initialized, `m.request` is called and its return value is assigned to `req`. Unlike the previous example, here there's no `initialValue`, so the `req` stream is in a pending state, and therefore has a value of `undefined`. `req.error` is the error stream for the request. Since `req` is pending, the `req.error` stream also remain in a pending state, and likewise, `vnode.state.error` stays pending and does not call `this.errorView`.
|
||||
|
||||
Then the component renders. Both `vnode.state.items()` and `vnode.state.error()` return `undefined`, so the component returns `[m(".loading-icon"), undefined]`, which in turn creates a loading icon element in the DOM.
|
||||
|
||||
When the request to the server completes, `req` is populated with the response data, which is propagated to the `vnode.state.items` dependent stream. (Note that the function in `catch` is not called if there's no error). After the request completes, the component is re-rendered. `vnode.state.error()` is still `undefined`, but now `view` returns a list of vnodes containing item names, and therefore the loading icon is replaced by a list of `div` elements are created in the DOM.
|
||||
|
||||
If the request to the server fails, `catch` is called and `vnode.state.items()` is set to an empty array. Also, `req.error` is populated with the error, and `vnode.state.error` is populated with the vnode tree returned by `errorView`. Therefore, `view` returns `[[], m(".error", "An error occurred")]`, which replaces the loading icon with the error message in the DOM.
|
||||
|
||||
---
|
||||
|
||||
### Dynamic URLs
|
||||
|
||||
Request URLs may contain interpolations:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue