remove default route, parse qs (#521)

This commit is contained in:
Yoshua Wuyts
2017-06-29 17:20:00 +02:00
committed by GitHub
parent 89e27dc512
commit 5d2104963c
6 changed files with 62 additions and 35 deletions
+5
View File
@@ -23,6 +23,11 @@ events: `'navigate'` will trigger whenever a route changes, `'replaceState'`
can be called to redirect routes, and `popState` which is emitted when the back can be called to redirect routes, and `popState` which is emitted when the back
button in the browser is pressed. button in the browser is pressed.
To top things off, we've reintroduced querystring parsing. An object containing
the current represenation of the search query (e.g. `?foo=bar`) can be found
under `state.query`. We used to do something similar in choo v4 and below, and
we're happy to reintroduce it in this release!
And that's about it - we've upgraded a whole slew of deps, and removed a few we And that's about it - we've upgraded a whole slew of deps, and removed a few we
didn't quite use. Overall we're quite proud of the new codebase, and filled didn't quite use. Overall we're quite proud of the new codebase, and filled
with joy we didn't have to make any changes to the API - additions only. with joy we didn't have to make any changes to the API - additions only.
+47 -24
View File
@@ -87,6 +87,7 @@
- [Philosophy](#philosophy) - [Philosophy](#philosophy)
- [Events](#events) - [Events](#events)
- [State](#state) - [State](#state)
- [Routing](#routing)
- [Server Rendering](#server-rendering) - [Server Rendering](#server-rendering)
- [Optimizations](#optimizations) - [Optimizations](#optimizations)
- [FAQ](#faq) - [FAQ](#faq)
@@ -224,9 +225,50 @@ The current params taken from the route. E.g. `/foo/:bar` becomes available as
`state.params.bar` If a wildcard route is used (`/foo/*`) it's available as `state.params.bar` If a wildcard route is used (`/foo/*`) it's available as
`state.params.wildcard`. `state.params.wildcard`.
### `state.query`
An object containing the current queryString. `/foo?bin=baz` becomes `{ bin:
'baz' }`.
### `state.route` ### `state.route`
The current name of the route used in the router (e.g. `/foo/:bar`). The current name of the route used in the router (e.g. `/foo/:bar`).
## Routing
Choo is an application level framework. This means that it takes care of
everything related to routing and pathnames for you.
### Params
Params can be registered by prepending the routename with `:routename`, e.g.
`/foo/:bar/:baz`. The value of the param will be saved on `state.params` (e.g.
`state.params.bar`). Wildcard routes can be registered with `*`, e.g. `/foo/*`.
The value of the wildcard will be saved under `state.params.wildcard`.
### Default routes
Sometimes a route doesn't match, and you want to display a page to handle it.
You can do this by declaring `app.route('*', handler)` to handle all routes
that didn't match anything else.
### Querystrings
Querystrings (e.g. `?foo=bar`) are ignored when matching routes. An object
containing the key-value mappings exists as `state.query`.
### Hash routing
Using hashes to delimit routes is supported out of the box (e.g. `/foo#bar`).
When a hash is found we also check if there's an available anchor on the same
page, and will scroll the screen to the position. Using both hashes in URLs and
anchor links on the page is generally not recommended.
### Following links
By default all clicks on `<a>` tags are handled by the router. If the `href=""`
attrbibute points to the same `origin` (e.g. the same page), and it doesn't
have a `rel="nooper"` attribute or similar, we handle the route. This can be
disabled application-wide by passing `{ href: false }` to the application
constructor. Uses [nanohref](https://github.com/yoshuawuyts/nanohref) under the
hood.
### Navigating programmatically
To can navigate routes you can emit `'pushState'`, `'popState'` or
`'replaceState'`. See [#events](#events) for more details about these events.
## Server Rendering ## Server Rendering
Choo was built with Node in mind. To render on the server call `.toString()` on Choo was built with Node in mind. To render on the server call `.toString()` on
your application. your application.
@@ -351,31 +393,12 @@ messages by calling `emitter.on()` and emit messages by calling
See [#events](#events) for an overview of all events. See [#events](#events) for an overview of all events.
### `app.route(routeName, handler)` ### `app.route(routeName, handler(state, emit))`
Register a route on the router. Uses [nanorouter][nanorouter] under the hood. Register a route on the router. The handler function is passed `app.state`
Params can be registered by prepending the routename with `:routename`, e.g. and `app.emitter.emit` as arguments. Uses [nanorouter][nanorouter] under the
`/foo/:bar/:baz`. The value of the param will be saved on `state.params` (e.g. hood.
`state.params.bar`). Wildcard routes can be registered with `*`, e.g. `/foo/*`.
The value of the wildcard will be saved under `state.params.wildcard`.
Using hashes to delimit routes is supported out of the box (e.g. `/foo#bar`). See [#routing](#routing) for an overview of how to use routing efficiently.
When a hash is found we also check if there's an available anchor on the same
page, and will scroll the screen to the position. Using both hashes in URLs and
anchor links on the page is generally not recommended.
New routes can be triggered through `emitter.emit('pushState', <routename>)`.
By default we also catch and match all `<a href="">` clicks against the router.
This can be disabled by setting `opts.href` to `false` in the constructor.
Routing via `pushState` will not work until the `DOMContentLoaded` event has
been fired.
If you need choo to ignore a particular route, you can add `data-no-routing`
attribute with `<a href="" data-no-routing>`. This is especially useful for
directing outside the choo app.
Querystrings (`?foo=bar`) are ignored when matching routes. They should be
extracted from the `window.location` object on render events, from either a
custom event listener or the matched views.
### `app.mount(selector)` ### `app.mount(selector)`
Start the application and mount it on the given `querySelector`. Uses Start the application and mount it on the given `querySelector`. Uses
+3 -4
View File
@@ -16,10 +16,8 @@ if (module.parent) {
var app = choo() var app = choo()
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
var persist = require('choo-persist') app.use(require('choo-persist')())
var logger = require('choo-log') app.use(require('choo-log')())
app.use(persist())
app.use(logger())
} }
app.use(expose()) app.use(expose())
app.use(todoStore) app.use(todoStore)
@@ -27,6 +25,7 @@ if (module.parent) {
app.route('/', mainView) app.route('/', mainView)
app.route('#active', mainView) app.route('#active', mainView)
app.route('#completed', mainView) app.route('#completed', mainView)
app.route('*', mainView)
app.mount('body') app.mount('body')
} }
+1 -1
View File
@@ -11,7 +11,7 @@
"dependencies": { "dependencies": {
"bel": "^4.5.1", "bel": "^4.5.1",
"choo-expose": "^1.0.0", "choo-expose": "^1.0.0",
"choo-log": "^6.0.0", "choo-log": "^7.0.0-0",
"choo-persist": "^3.0.0", "choo-persist": "^3.0.0",
"microbounce": "^1.0.0", "microbounce": "^1.0.0",
"microframe": "^1.0.0", "microframe": "^1.0.0",
+5 -6
View File
@@ -3,6 +3,7 @@ var documentReady = require('document-ready')
var nanotiming = require('nanotiming') var nanotiming = require('nanotiming')
var nanorouter = require('nanorouter') var nanorouter = require('nanorouter')
var nanomorph = require('nanomorph') var nanomorph = require('nanomorph')
var nanoquery = require('nanoquery')
var nanohref = require('nanohref') var nanohref = require('nanohref')
var nanoraf = require('nanoraf') var nanoraf = require('nanoraf')
var nanobus = require('nanobus') var nanobus = require('nanobus')
@@ -18,11 +19,6 @@ function Choo (opts) {
assert.equal(typeof opts, 'object', 'choo: opts should be type object') assert.equal(typeof opts, 'object', 'choo: opts should be type object')
var routerOpts = {
default: opts.defaultRoute || '/404',
curry: true
}
// define events used by choo // define events used by choo
this._events = { this._events = {
DOMCONTENTLOADED: 'DOMContentLoaded', DOMCONTENTLOADED: 'DOMContentLoaded',
@@ -39,7 +35,7 @@ function Choo (opts) {
this._tree = null this._tree = null
// properties that are part of the API // properties that are part of the API
this.router = nanorouter(routerOpts) this.router = nanorouter({ curry: true })
this.emitter = nanobus('choo.emit') this.emitter = nanobus('choo.emit')
this.state = { events: this._events } this.state = { events: this._events }
} }
@@ -78,6 +74,7 @@ Choo.prototype.start = function () {
if (this._historyEnabled) { if (this._historyEnabled) {
this.emitter.prependListener(this._events.NAVIGATE, function () { this.emitter.prependListener(this._events.NAVIGATE, function () {
self.emitter.emit(self._events.RENDER) self.emitter.emit(self._events.RENDER)
self.state.query = nanoquery(window.location.search)
setTimeout(scrollToAnchor.bind(null, window.location.hash), 0) setTimeout(scrollToAnchor.bind(null, window.location.hash), 0)
}) })
@@ -113,6 +110,7 @@ Choo.prototype.start = function () {
var location = this._createLocation() var location = this._createLocation()
this._tree = this.router(location) this._tree = this.router(location)
this.state.query = nanoquery(window.location.search)
assert.ok(this._tree, 'choo.start: no valid DOM node returned for location ' + location) assert.ok(this._tree, 'choo.start: no valid DOM node returned for location ' + location)
this.emitter.prependListener(self._events.RENDER, nanoraf(function () { this.emitter.prependListener(self._events.RENDER, nanoraf(function () {
@@ -169,6 +167,7 @@ Choo.prototype.toString = function (location, state) {
assert.equal(typeof location, 'string', 'choo.toString: location should be type string') assert.equal(typeof location, 'string', 'choo.toString: location should be type string')
assert.equal(typeof this.state, 'object', 'choo.toString: state should be type object') assert.equal(typeof this.state, 'object', 'choo.toString: state should be type object')
this.state.query = nanoquery(location)
var html = this.router(location) var html = this.router(location)
assert.ok(html, 'choo.toString: no valid value returned for the route ' + location) assert.ok(html, 'choo.toString: no valid value returned for the route ' + location)
return html.toString() return html.toString()
+1
View File
@@ -27,6 +27,7 @@
"nanobus": "^4.2.0", "nanobus": "^4.2.0",
"nanohref": "^2.0.0", "nanohref": "^2.0.0",
"nanomorph": "^5.1.2", "nanomorph": "^5.1.2",
"nanoquery": "^1.1.0",
"nanoraf": "^3.0.0", "nanoraf": "^3.0.0",
"nanorouter": "^2.0.0", "nanorouter": "^2.0.0",
"nanotiming": "^6.0.0", "nanotiming": "^6.0.0",