fantasy-land methods no longer absorb streams
This commit is contained in:
parent
7fa3fee9ce
commit
015a812610
4 changed files with 524 additions and 290 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
- [API](#api)
|
||||
- [How it works](#how-it-works)
|
||||
- [Typical workflow](#typical-workflow)
|
||||
- [Typical usage](#typical-usage)
|
||||
- [Dynamic URLs](#dynamic-urls)
|
||||
- [Why JSON instead of HTML](#why-json-instead-of-html)
|
||||
- [Why XMLHttpRequest instead of fetch](#why-xmlhttprequest-instead-of-fetch)
|
||||
|
|
@ -43,7 +43,7 @@ The `m.request` utility is a thin wrapper around [`XMLHttpRequest`](https://deve
|
|||
m.request({
|
||||
method: "GET",
|
||||
url: "/api/v1/users",
|
||||
}).map(function(users) {
|
||||
}).run(function(users) {
|
||||
console.log(users)
|
||||
})
|
||||
```
|
||||
|
|
@ -52,7 +52,7 @@ Calls to `m.request` return a [stream](prop.md).
|
|||
|
||||
---
|
||||
|
||||
### Typical workflow
|
||||
### Typical usage
|
||||
|
||||
Here's an illustrative example of a self-contained component that uses `m.request` to retrieve some data from a server.
|
||||
|
||||
|
|
@ -81,6 +81,8 @@ Let's assume making a request to the server URL `/api/items` returns an array of
|
|||
|
||||
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.
|
||||
|
||||
#### Loading icons and error messages
|
||||
|
||||
Here's an expanded version of the example above that implements a loading indicator and an error message:
|
||||
|
||||
```javascript
|
||||
|
|
@ -93,7 +95,7 @@ var RobustExample = {
|
|||
vnode.state.items = req.catch(function() {
|
||||
return []
|
||||
})
|
||||
vnode.state.error = req.error.map(this.errorView)
|
||||
vnode.state.error = req.error.run(this.errorView)
|
||||
},
|
||||
view: function(vnode) {
|
||||
return [
|
||||
|
|
@ -132,7 +134,7 @@ m.request({
|
|||
method: "GET",
|
||||
url: "/api/v1/users/:id",
|
||||
data: {id: 123},
|
||||
}).map(function(user) {
|
||||
}).run(function(user) {
|
||||
console.log(user.id) // logs 123
|
||||
})
|
||||
```
|
||||
|
|
@ -279,9 +281,9 @@ function getProjectOwnerID(project) {
|
|||
|
||||
function doStuffWithProjectOwner(projectID) {
|
||||
return findProject(projectID)
|
||||
.map(getProjectOwnerID)
|
||||
.map(findUser)
|
||||
.map(doStuff)
|
||||
.run(getProjectOwnerID)
|
||||
.run(findUser)
|
||||
.run(doStuff)
|
||||
.catch(function(e) {
|
||||
console.log(e)
|
||||
})
|
||||
|
|
@ -290,7 +292,7 @@ function doStuffWithProjectOwner(projectID) {
|
|||
doStuffWithProjectOwner(123)
|
||||
```
|
||||
|
||||
Aside from the API signature difference between `fetch` and `m.request`, the only change required to achieve the same functionality was to replace all instances of `then` with `map`.
|
||||
Aside from the API signature difference between `fetch` and `m.request`, the only change required to achieve the same functionality was to replace all instances of `.then` with `.run`.
|
||||
|
||||
However, stream have some additional interesting properties. Let's suppose project objects have a `team` property that contains a list of user objects, and we wanted to display a list of designers and a list of developers in a project:
|
||||
|
||||
|
|
@ -304,11 +306,11 @@ function getTeamUsersByType(team, type) {
|
|||
})
|
||||
}
|
||||
var project = findProject(123)
|
||||
var team = project.map(getProjectTeam)
|
||||
var designers = team.map(function(team) {
|
||||
var team = project.run(getProjectTeam)
|
||||
var designers = team.run(function(team) {
|
||||
return getTeamUsersByType(team, "designer")
|
||||
})
|
||||
var developers = team.map(function(team) {
|
||||
var developers = team.run(function(team) {
|
||||
return getTeamUsersByType(team, "developer")
|
||||
})
|
||||
```
|
||||
|
|
@ -318,7 +320,7 @@ Now let's suppose that the team changed for the project and we need to fetch the
|
|||
Fortunately, `project` is a stream, and `team`, `designers` and `developers` are streams derived from `project`. So to update the state of all these streams, we only need to do this:
|
||||
|
||||
```javascript
|
||||
findProject(123).map(project)
|
||||
findProject(123).run(project)
|
||||
```
|
||||
|
||||
Doing so updates all the streams, and therefore there's no need to place the filtering code in the view, where the filtering code would recompute the same thing on every render.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue