2016-10-11 18:53:32 +00:00
|
|
|
# angel_route
|
2016-11-23 09:13:42 +00:00
|
|
|
|
2017-03-20 19:46:54 +00:00
|
|
|
![version 1.0.3](https://img.shields.io/badge/version-1.0.3-brightgreen.svg)
|
2016-11-23 09:13:42 +00:00
|
|
|
![build status](https://travis-ci.org/angel-dart/route.svg)
|
|
|
|
|
2016-10-12 17:58:32 +00:00
|
|
|
A powerful, isomorphic routing library for Dart.
|
|
|
|
|
|
|
|
This API is a huge improvement over the original [Angel](https://github.com/angel-dart/angel)
|
|
|
|
routing system, and thus deserves to be its own individual project.
|
|
|
|
|
|
|
|
`angel_route` exposes a routing system that takes the shape of a tree. This tree structure
|
|
|
|
can be easily navigated, in a fashion somewhat similar to a filesystem. The `Router` API
|
|
|
|
is a very straightforward interface that allows for your code to take a shape similar to
|
|
|
|
the route tree. Users of Laravel and Express will be very happy.
|
|
|
|
|
|
|
|
`angel_route` does not require the use of [Angel](https://github.com/angel-dart/angel),
|
|
|
|
and has no dependencies. Thus, it can be used in any application, regardless of
|
|
|
|
framework. This includes Web apps, Flutter apps, CLI apps, and smaller servers which do
|
|
|
|
not need all the features of the Angel framework.
|
|
|
|
|
|
|
|
# Contents
|
|
|
|
|
|
|
|
* [Examples](#examples)
|
|
|
|
* [Routing](#routing)
|
|
|
|
* [Tree Hierarchy and Path Resolution](#hierarchy)
|
|
|
|
* [In the Browser](#in-the-browser)
|
|
|
|
* [Route State](#route-state)
|
|
|
|
* [Route Parameters](#route-parameters)
|
|
|
|
|
|
|
|
# Examples
|
|
|
|
|
|
|
|
## Routing
|
|
|
|
If you use [Angel](https://github.com/angel-dart/angel), every `Angel` instance is
|
|
|
|
a `Router` in itself.
|
|
|
|
|
|
|
|
```dart
|
|
|
|
|
|
|
|
main() {
|
|
|
|
final router = new Router();
|
|
|
|
|
|
|
|
router.get('/users', () {});
|
|
|
|
|
|
|
|
router.post('/users/:id/timeline', (String id) {});
|
|
|
|
|
2016-10-13 09:52:56 +00:00
|
|
|
router.get('/square_root/:id([0-9]+)', (n) {
|
2016-11-27 23:39:03 +00:00
|
|
|
return { 'result': pow(int.parse(n), 0.5) };
|
2016-10-12 17:58:32 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
router.group('/show/:id', (router) {
|
|
|
|
router.get('/reviews', (id) {
|
|
|
|
return someQuery(id).reviews;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Optionally restrict params to a RegExp
|
|
|
|
router.get('/reviews/:reviewId([A-Za-z0-9_]+)', (id, reviewId) {
|
|
|
|
return someQuery(id).reviews.firstWhere(
|
|
|
|
(r) => r.id == reviewId);
|
|
|
|
});
|
2016-10-19 22:04:06 +00:00
|
|
|
}, middleware: [put, middleware, here]);
|
2016-10-12 17:58:32 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The default `Router` does not give any notification of routes being changed, because
|
|
|
|
there is no inherent stream of URL's for it to listen to. This is good, because a server
|
|
|
|
needs a lot of flexibility with which to handle requests.
|
|
|
|
|
|
|
|
## Hierarchy
|
|
|
|
|
|
|
|
```dart
|
|
|
|
main() {
|
|
|
|
final foo = new Route('/');
|
|
|
|
final bar = foo.child('bar');
|
|
|
|
final baz = foo.child('baz');
|
|
|
|
|
|
|
|
final a = bar.child('a');
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Relative paths:
|
|
|
|
* a.resolve('../baz') = baz;
|
|
|
|
* bar.resolve('a') = a;
|
|
|
|
*
|
|
|
|
* Absolute paths:
|
|
|
|
* a.resolve('/bar/a') = a;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
```dart
|
|
|
|
main() {
|
|
|
|
final router = new Router();
|
|
|
|
|
2016-11-27 22:24:30 +00:00
|
|
|
router
|
|
|
|
.chain('middleware1')
|
|
|
|
.chain('other_middleware')
|
|
|
|
.get('/hello', () {
|
|
|
|
print('world');
|
|
|
|
});
|
|
|
|
|
2016-10-12 17:58:32 +00:00
|
|
|
router.group('/user/:id', (router) {
|
|
|
|
router.get('/balance', (id) async {
|
|
|
|
final user = await someQuery(id);
|
|
|
|
return user.balance;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
See [the tests](test/route/no_params.dart) for good examples.
|
|
|
|
|
|
|
|
# In the Browser
|
|
|
|
Supports both hashed routes and pushState. The `BrowserRouter` interface exposes
|
2016-11-27 22:24:30 +00:00
|
|
|
a `Stream<RoutingResult> onRoute`, which can be listened to for changes. It will fire `null`
|
2016-10-12 17:58:32 +00:00
|
|
|
whenever no route is matched.
|
|
|
|
|
|
|
|
`angel_route` will also automatically intercept `<a>` elements and redirect them to
|
|
|
|
your routes.
|
|
|
|
|
|
|
|
To prevent this for a given anchor, do any of the following:
|
|
|
|
* Do not provide an `href`
|
|
|
|
* Provide a `download` or `target` attribute on the element
|
|
|
|
* Set `rel="external"`
|
|
|
|
|
|
|
|
# Route State
|
|
|
|
|
|
|
|
```dart
|
|
|
|
main() {
|
|
|
|
final router = new BrowserRouter();
|
|
|
|
// ..
|
|
|
|
router.onRoute.listen((route) {
|
|
|
|
if (route == null)
|
|
|
|
throw 404;
|
2016-10-13 20:50:27 +00:00
|
|
|
else route.state['foo'] = 'bar';
|
2016-10-12 17:58:32 +00:00
|
|
|
});
|
2017-03-20 19:46:54 +00:00
|
|
|
|
|
|
|
router.listen(); // Start listening
|
2016-10-12 17:58:32 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2016-11-27 22:24:30 +00:00
|
|
|
For applications where you need to access a chain of handlers, consider using
|
|
|
|
`onResolve` instead. You can see an example in `web/shared/basic.dart`.
|
|
|
|
|
2016-10-12 17:58:32 +00:00
|
|
|
# Route Parameters
|
|
|
|
Routes can have parameters, as seen in the above examples.
|
2017-06-01 12:47:16 +00:00
|
|
|
Use [allParams](https://www.dartdocs.org/documentation/angel_route/1.0.3/angel_route/RoutingResult-class.html)
|
|
|
|
in a `RoutingResult` to get them as a nice `Map`:
|
|
|
|
|
|
|
|
```dart
|
|
|
|
var router = new Router();
|
|
|
|
router.get('/book/:id/authors', () => ...);
|
|
|
|
|
|
|
|
var result = router.resolve('/book/foo/authors');
|
|
|
|
var params = result.allParams; // {'id': 'foo'};
|
|
|
|
```
|