diff --git a/CHANGELOG.md b/CHANGELOG.md index 32deb72..07354c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 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 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. diff --git a/README.md b/README.md index 4422a59..a9d7d9b 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ - [Philosophy](#philosophy) - [Events](#events) - [State](#state) +- [Routing](#routing) - [Server Rendering](#server-rendering) - [Optimizations](#optimizations) - [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.wildcard`. +### `state.query` +An object containing the current queryString. `/foo?bin=baz` becomes `{ bin: +'baz' }`. + ### `state.route` 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 `` 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 Choo was built with Node in mind. To render on the server call `.toString()` on 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. -### `app.route(routeName, handler)` -Register a route on the router. Uses [nanorouter][nanorouter] under the hood. -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`. +### `app.route(routeName, handler(state, emit))` +Register a route on the router. The handler function is passed `app.state` +and `app.emitter.emit` as arguments. Uses [nanorouter][nanorouter] under the +hood. -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. - -New routes can be triggered through `emitter.emit('pushState', )`. -By default we also catch and match all `` 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 ``. 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. +See [#routing](#routing) for an overview of how to use routing efficiently. ### `app.mount(selector)` Start the application and mount it on the given `querySelector`. Uses diff --git a/example/index.js b/example/index.js index 9bf639e..08fdba8 100644 --- a/example/index.js +++ b/example/index.js @@ -16,10 +16,8 @@ if (module.parent) { var app = choo() if (process.env.NODE_ENV !== 'production') { - var persist = require('choo-persist') - var logger = require('choo-log') - app.use(persist()) - app.use(logger()) + app.use(require('choo-persist')()) + app.use(require('choo-log')()) } app.use(expose()) app.use(todoStore) @@ -27,6 +25,7 @@ if (module.parent) { app.route('/', mainView) app.route('#active', mainView) app.route('#completed', mainView) + app.route('*', mainView) app.mount('body') } diff --git a/example/package.json b/example/package.json index 1a3719b..111ca0b 100644 --- a/example/package.json +++ b/example/package.json @@ -11,7 +11,7 @@ "dependencies": { "bel": "^4.5.1", "choo-expose": "^1.0.0", - "choo-log": "^6.0.0", + "choo-log": "^7.0.0-0", "choo-persist": "^3.0.0", "microbounce": "^1.0.0", "microframe": "^1.0.0", diff --git a/index.js b/index.js index f07ea42..b0bffc6 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ var documentReady = require('document-ready') var nanotiming = require('nanotiming') var nanorouter = require('nanorouter') var nanomorph = require('nanomorph') +var nanoquery = require('nanoquery') var nanohref = require('nanohref') var nanoraf = require('nanoraf') var nanobus = require('nanobus') @@ -18,11 +19,6 @@ function Choo (opts) { assert.equal(typeof opts, 'object', 'choo: opts should be type object') - var routerOpts = { - default: opts.defaultRoute || '/404', - curry: true - } - // define events used by choo this._events = { DOMCONTENTLOADED: 'DOMContentLoaded', @@ -39,7 +35,7 @@ function Choo (opts) { this._tree = null // properties that are part of the API - this.router = nanorouter(routerOpts) + this.router = nanorouter({ curry: true }) this.emitter = nanobus('choo.emit') this.state = { events: this._events } } @@ -78,6 +74,7 @@ Choo.prototype.start = function () { if (this._historyEnabled) { this.emitter.prependListener(this._events.NAVIGATE, function () { self.emitter.emit(self._events.RENDER) + self.state.query = nanoquery(window.location.search) setTimeout(scrollToAnchor.bind(null, window.location.hash), 0) }) @@ -113,6 +110,7 @@ Choo.prototype.start = function () { var location = this._createLocation() 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) 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 this.state, 'object', 'choo.toString: state should be type object') + this.state.query = nanoquery(location) var html = this.router(location) assert.ok(html, 'choo.toString: no valid value returned for the route ' + location) return html.toString() diff --git a/package.json b/package.json index 45d8b5e..0bc27d1 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "nanobus": "^4.2.0", "nanohref": "^2.0.0", "nanomorph": "^5.1.2", + "nanoquery": "^1.1.0", "nanoraf": "^3.0.0", "nanorouter": "^2.0.0", "nanotiming": "^6.0.0",