Skip to content

Commit

Permalink
composable views
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshuawuyts committed Jan 14, 2018
1 parent 2235b8d commit ae7be20
Showing 1 changed file with 67 additions and 1 deletion.
68 changes: 67 additions & 1 deletion content/reference/views.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ listeners can in turn emit events on the event bus. To make sure that the
application's flow is easy to reason about, views cannot attach listeners on
the event bus themselves. This is where "data down, events up" becomes
visible in the code: the view only has access to `emit()`, while anything that is
declared through `app.use()`, such as a store, has access to the whole event bus
declared through `app.use()`, such as a store, has access to the whole event bus
throught `emitter`. This can both send events with `emitter.emit()`, as well as
receive them with `emitter.on()`.

Expand Down Expand Up @@ -204,3 +204,69 @@ module.exports = function (state, emit) {
showing the same title we already have, then we don't need to do anything.
Conditional statements are less instructions on the CPU than function calls,
so this provides us with a little speedup (and less noise).

## Composable Views
As applications grow, you'll probably find that there will be plenty of views
that share a lot of the same layout. If you find that you're repeating the same
layout in a lot of code, it can be beneficial to make it reusable instead.

The most common way to create reusable templates, is to create a function that
takes a view as an argument and returns a view. Inside the function the
childView is called, and wrapped with some HTML. The end result is a nicely
composed function that's also known as a "composable view", "higher order view",
"template".

This might sound a little abstract. So let's create an example higher order
view, which has a static header and footer, but takes the content as an
argument.

_note: There's not an exact word for what we're doing here. Because it's a
pattern, and not an API the exact word also doesn't matter too much. This is
also not at all the only way to compose functions - so don't worry too much
about getting the terminology right - we're also just making it up as we go._

```js
var html = require('choo/html')
var choo = require('choo')

var app = choo()
app.route('/', template(main)) // 1.
app.route('/bar', template(bar)) // 2.
app.mount('body')

function template (childView) { // 3.
return (state, emit) => { // 4.
return html`
<body>
<header>This is the header</header>
${childView(state, emit)}
<footer>This is the footer</footer>
</body>
`
}
}

function main (state, emit) { // 5.
return html`
<h1>I'm the main view</h1>
`
}

function foo (state, emit) { // 6.
return html`
<h1>fooooooooooo view</h1>
`
}
```

1. We create a route `'/'` which calls the `template` function, and passes it
the `main` view function.
2. We create a route `'/foo'` which calls the `template` function, and passes it
the `foo` view function.
3. This is where the bulk of the action happens. We create a function named
`'template'` which takes a view as an argument (`childView`).
4. The `'template'` function returns another function. This is the function
we'll be passing to `app.route()`. It's a valid view. When the view is
called, it calls the `childView`, and wraps it with DOM elements.
5. We define a view named `'main'`.
6. We define a view named `'foo'`.

0 comments on commit ae7be20

Please sign in to comment.