route.dart
route.dart copied to clipboard
`null` in the URL if `path` is not specified for a route
Consider I want to have the following hierarchy of routes in my app:
-
/products
- is the common URL for all product-related pages. Serves page with all available items listed. Naturally it should become a parent route for page-specific mounted sub-routes. -
/products/:id
- should become a mounted route of/products
and display product details page.
1. Straightforward approach
Straightforward routing configuration would look like this:
_initRouter(Router router, RouteViewFactory views) {
views.configure({
'product': ngRoute(
path: '/products',
view: 'views/products.html',
modules: () => [new ProductModule()],
mount: {
'details': ngRoute(
path: '/:id',
view: 'views/product_details.html')
})
});
}
But as we found out, a route with mounted routes, always displays its own view, mounted routes' views are never displayed. And this is by design.
2. Flattening routes
What are available workarounds?! Flatten the route hierarchy:
_initRouter(Router router, RouteViewFactory views) {
views.configure({
'product_list': ngRoute(
path: '/products',
view: 'views/products.html',
modules: () => [new ProductModule()]),
'product_details': ngRoute(
path: '/products/:id',
view: 'views/product_details.html',
modules: () => [new ProductModule()])
});
}
But this way I have my modules instantiated twice. All the services that are expected to be singletons now have multiple instances and can't be used to share the data. To workaround the workaround I would need to cache the module instances. And generally flattening kills the idea of structuring your routes using route hierarchies.
3. Third take
Turns out there is an elegant solution. If we set defaultRoute: true
for a mounted route, we can host its view on the parents URL:
_initRouter(Router router, RouteViewFactory views) {
views.configure({
'product': ngRoute(
path: '/products',
viewHtml: '<ng-view></ng-view>',
modules: () => [new ProductModule()],
mount: {
'list': ngRoute(
defaultRoute: true,
view: 'views/products.html'),
'details': ngRoute(
path: '/:id',
view: 'views/product_details.html')
})
});
}
Now everything looks good, we leverage the routes hierarchy to avoid duplicating common path parts and dependent modules declaration, have a single instance of each dependent module created and fully satisfy original requirements - don't have any redundant sub-paths (i.e. adopting this approach would lead to a redundant sub-path /products/list
)
Finally we come closer to the originally reported issue :). If we use the latest configuration we have null
inside the URL when we try to navigate to product.list
route using Router
's go()
method:
router.go('product.list') // => http://localhost:8080/productsnull
Right now to workaround this behavior we can add path
to the product.list
route configuration:
// configuration
'list': ngRoute(
defaultRoute: true,
path: '',
view: 'views/products.html')
// usage
router.go('product.list') // => http://localhost:8080/product
or:
// configuration
'list': ngRoute(
defaultRoute: true,
path: '/',
view: 'views/products.html')
// usage
router.go('product.list') // => http://localhost:8080/product/
In the last example we must have defaultRoute: true
defined, otherwise users won't be able to navigate to the product list page if they forget to type /
at the end of the URL in the browser's address bar.
Basically path is required, and ngRoute should complain if path is not provided.
@antonmoiseev Thank you for all the detailed issues you've been posting. I'm tearing my hair out trying to figure out Angular.dart routing, and after reading several of your issues, I am finally starting to figure it out.
You are welcome! BTW, we ended up flattening all our routes. We experienced an issue with navigation when typing in URL manually in the address bar and with blank pages in IE 10 and above.
I tried flattening my routes this morning and already a lot of the strange behavior is gone! (Particularly the watchQueryParameters
, which I couldn't get to work correctly on a nested route.) Thank you again.