diff --git a/example/main.dart b/example/main.dart new file mode 100644 index 00000000..98a471e3 --- /dev/null +++ b/example/main.dart @@ -0,0 +1,48 @@ +import 'dart:math'; + +import 'package:angel_route/angel_route.dart'; + +main() { + final router = new Router(); + + router.get('/users', () {}); + + router.post('/users/:id/timeline', (String id) {}); + + router.get('/square_root/:id([0-9]+)', (String n) { + return {'result': pow(int.parse(n), 0.5)}; + }); + + // You can also have parameters auto-parsed. + // + // Supports int, double, and num. + router.get('/square_root/int:id([0-9]+)', (int n) { + return {'result': pow(n, 0.5)}; + }); + + 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); + }); + }); +} + +SomeQuery someQuery(id) => new SomeQuery(); + +class SomeQuery { + List get reviews => [ + SomeQueryReview('fake'), + SomeQueryReview('data'), + ]; +} + +class SomeQueryReview { + final String id; + + SomeQueryReview(this.id); +} diff --git a/lib/browser.dart b/lib/browser.dart index 97ac1d34..01b77cde 100644 --- a/lib/browser.dart +++ b/lib/browser.dart @@ -1,25 +1,27 @@ import 'dart:async' show Stream, StreamController; import 'dart:html'; -import 'angel_route.dart'; + import 'package:path/path.dart' as p; +import 'angel_route.dart'; + final RegExp _hash = new RegExp(r'^#/'); final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)'); /// A variation of the [Router] support both hash routing and push state. -abstract class BrowserRouter extends Router { +abstract class BrowserRouter extends Router { /// Fires whenever the active route changes. Fires `null` if none is selected (404). - Stream get onResolve; + Stream> get onResolve; /// Fires whenever the active route changes. Fires `null` if none is selected (404). - Stream get onRoute; + Stream> get onRoute; /// Set `hash` to true to use hash routing instead of push state. /// `listen` as `true` will call `listen` after initialization. factory BrowserRouter({bool hash: false, bool listen: false}) { return hash - ? new _HashRouter(listen: listen) - : new _PushStateRouter(listen: listen); + ? new _HashRouter(listen: listen) + : new _PushStateRouter(listen: listen); } BrowserRouter._() : super(); @@ -39,22 +41,24 @@ abstract class BrowserRouter extends Router { void listen(); /// Identical to [all]. - Route on(String path, handler, {List middleware}); + Route on(String path, T handler, {Iterable middleware}); } -abstract class _BrowserRouterImpl extends Router implements BrowserRouter { +abstract class _BrowserRouterImpl extends Router + implements BrowserRouter { bool _listening = false; Route _current; - StreamController _onResolve = - new StreamController(); - StreamController _onRoute = new StreamController(); + StreamController> _onResolve = + new StreamController>(); + StreamController> _onRoute = new StreamController>(); + Route get currentRoute => _current; @override - Stream get onResolve => _onResolve.stream; + Stream> get onResolve => _onResolve.stream; @override - Stream get onRoute => _onRoute.stream; + Stream> get onRoute => _onRoute.stream; _BrowserRouterImpl({bool listen}) : super() { if (listen != false) this.listen(); @@ -64,11 +68,11 @@ abstract class _BrowserRouterImpl extends Router implements BrowserRouter { @override void go(Iterable linkParams) => _goTo(navigate(linkParams)); - Route on(String path, handler, {List middleware}) => + Route on(String path, T handler, {Iterable middleware}) => all(path, handler, middleware: middleware); void prepareAnchors() { - final anchors = window.document.querySelectorAll('a:not([dynamic])'); + final anchors = window.document.querySelectorAll('a'); //:not([dynamic])'); for (final AnchorElement $a in anchors) { if ($a.attributes.containsKey('href') && @@ -77,7 +81,8 @@ abstract class _BrowserRouterImpl extends Router implements BrowserRouter { $a.attributes['rel'] != 'external') { $a.onClick.listen((e) { e.preventDefault(); - go($a.attributes['href'].split('/').where((str) => str.isNotEmpty)); + _goTo($a.attributes['href']); + //go($a.attributes['href'].split('/').where((str) => str.isNotEmpty)); }); } @@ -96,7 +101,7 @@ abstract class _BrowserRouterImpl extends Router implements BrowserRouter { } } -class _HashRouter extends _BrowserRouterImpl { +class _HashRouter extends _BrowserRouterImpl { _HashRouter({bool listen}) : super(listen: listen) { if (listen) this.listen(); } @@ -108,7 +113,9 @@ class _HashRouter extends _BrowserRouterImpl { void handleHash([_]) { final path = window.location.hash.replaceAll(_hash, ''); - final resolved = resolveAbsolute(path).first; + var allResolved = resolveAbsolute(path); + + final resolved = allResolved.isEmpty ? null : allResolved.first; if (resolved == null) { _onResolve.add(null); @@ -138,7 +145,7 @@ class _HashRouter extends _BrowserRouterImpl { } } -class _PushStateRouter extends _BrowserRouterImpl { +class _PushStateRouter extends _BrowserRouterImpl { String _basePath; _PushStateRouter({bool listen, Route root}) : super(listen: listen) { @@ -165,10 +172,8 @@ class _PushStateRouter extends _BrowserRouterImpl { _onRoute.add(_current = null); } else { final route = resolved.route; - window.history.pushState( - {'path': route.path, 'params': {}}, - route.name ?? route.path, - relativeUri); + window.history.pushState({'path': route.path, 'params': {}}, + route.name ?? route.path, relativeUri); _onResolve.add(resolved); _onRoute.add(_current = route); } diff --git a/lib/src/router.dart b/lib/src/router.dart index 052e9732..cbc0e62e 100644 --- a/lib/src/router.dart +++ b/lib/src/router.dart @@ -25,7 +25,7 @@ final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)'); /// An abstraction over complex [Route] trees. Use this instead of the raw API. :) class Router { - final Map> _cache = {}; + final Map>> _cache = {}; //final List<_ChainedRouter> _chained = []; final List _middleware = []; @@ -142,7 +142,7 @@ class Router { if (route is! SymlinkRoute) buf.write('${route.method} '); buf.write('${route.path.isNotEmpty ? route.path : '/'}'); - if (route is SymlinkRoute) { + if (route is SymlinkRoute) { buf.writeln(); dumpRouter(route.router); } else { @@ -212,7 +212,7 @@ class Router { segments.add(route.path.replaceAll(_straySlashes, '')); lastRoute = route; - if (route is SymlinkRoute) { + if (route is SymlinkRoute) { search = route.router; } @@ -230,7 +230,7 @@ class Router { segments.add(route.path.replaceAll(_straySlashes, '')); lastRoute = route; - if (route is SymlinkRoute) { + if (route is SymlinkRoute) { search = route.router; } @@ -279,7 +279,7 @@ class Router { for (Route route in r.routes) { int pos = scanner.position; - if (route is SymlinkRoute) { + if (route is SymlinkRoute) { if (route.parser.parse(scanner).successful) { var s = crawl(route.router); if (s) success = true; @@ -311,13 +311,13 @@ class Router { /// Returns the result of [resolve] with [path] passed as /// both `absolute` and `relative`. - Iterable resolveAbsolute(String path, + Iterable> resolveAbsolute(String path, {String method: 'GET', bool strip: true}) => resolveAll(path, path, method: method, strip: strip); /// Finds every possible [Route] that matches the given path, /// with the given method. - Iterable resolveAll(String absolute, String relative, + Iterable> resolveAll(String absolute, String relative, {String method: 'GET', bool strip: true}) { if (_useCache == true) { return _cache.putIfAbsent('$method$absolute', @@ -327,9 +327,9 @@ class Router { return _resolveAll(absolute, relative, method: method, strip: strip); } - Iterable _resolveAll(String absolute, String relative, + Iterable> _resolveAll(String absolute, String relative, {String method: 'GET', bool strip: true}) { - final List results = []; + var results = >[]; resolve(absolute, relative, results, method: method, strip: strip); // _printDebug( @@ -435,11 +435,11 @@ class _ChainedRouter extends Router { } /// Optimizes a router by condensing all its routes into one level. -Router flatten(Router router) { - var flattened = new Router(); +Router flatten(Router router) { + var flattened = new Router(); for (var route in router.routes) { - if (route is SymlinkRoute) { + if (route is SymlinkRoute) { var base = route.path.replaceAll(_straySlashes, ''); var child = flatten(route.router); diff --git a/web/hash/basic.html b/web/hash/basic.html index e2e0e86b..ea7fbaf0 100644 --- a/web/hash/basic.html +++ b/web/hash/basic.html @@ -25,7 +25,6 @@
  • (empty)
- - + \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 00000000..4806c9ad --- /dev/null +++ b/web/index.html @@ -0,0 +1,15 @@ + + + + + + Angel Route Samples + + +

Angel Route Samples

+ + + \ No newline at end of file diff --git a/web/push_state/basic.html b/web/push_state/basic.html index 378569b0..953bc5a2 100644 --- a/web/push_state/basic.html +++ b/web/push_state/basic.html @@ -26,7 +26,6 @@
  • (empty)
- - + \ No newline at end of file