ISO: Moon Shots

Sorry I’m late to the party. Here’s my wish: I’d love to see a better/different router!

I really like Aurelia. I’ve migrated two medium-sized apps from AngularJS last year. Now we have four regularly updated Aurelia apps in production and I use it for all new projects.

Everything that Aurelia couldn’t do well out of the box I was able to fix with custom components, attributes, ValidationRenderers, Webpack plugins, etc. :ok_hand:except the router.

The old Angular-UI-Router does several things much better than current Aurelia – things that are very difficult to fix with custom code without rewriting large parts of the router:

  1. Resolving child routes by name, i.e., I’m at root.childState1 and want to navigate to root.childState2.subchild1. I’m not aware of any way to do this in Aurelia.

    root.childState1 has to know the full URL for root.childState2.subchild1, all its parameters, where they have to go in the URL, and how to serialize them (see below). If any of these properties change, you’re hosed. And you have to manually construct that URL every time you want to link to that state.

    This breaks state encapsulation and spreads knowledge about a state across all other states and templates that may want to link to that state. In a large, cross-linked application, this creates a nightmare of brittle references.

  2. Relative routes: Building on the previous example, it’s often really useful to reference root.childState2.subchild1 relatively, eg., ^.childState2.subchild1. Aurelia has something similar (letting you specify just the name of a parent to navigate to it), but it’s way less powerful.

  3. Route parameter types and default values: navigateToRoute has no concept of parameter conversion to/from the URL and simply converts all parameters to strings.

    An example: We often have date, time or timestamp parameters for routes. They are usually moment or Date objects, but in URLs they have to be formatted, eg., as YYYY-MM-DD or HH:mm, depending on the use-case.

    It’s fairly easy to automatically handle these URL-to-model case by adding a converter to the routing pipeline using addAuthorizeStep. This central converter can ask the target state for its parameter types and default values and take care of the rest – so that activate already gets validated, converted parameters.

    But there’s no way of doing the same for everyone who links to that state. Everyone who uses route-href or router.navigateToRoute has to know the target format and manually convert all parameters. Aurelia has no way to look up child states (see point 1), so there’s no way to get the target state’s parameter types to automatically convert them.

For comparison, in ancient AngularJS, I could simply say $state.go('root.child2.subchild1', params) or <a ui-sref="root.child2.subchild1(params)">. Angular-UI-Router would…

  • look up the state definitions for root, child2 and subchild1
  • look at all their parameter types
  • convert the given params to strings based on these parameter types, omit default values, etc. and create the URL accordingly (… and would throw a meaningful error if, eg., the state was not known or a required parameter was missing or malformed.)

The problem is that configureRouter() (to add sub-states) is part of the state object itself – instead of being its own thing – so Aurelia would have to eagerly load and instantiate all states to build the full state tree.

If route configuration wasn’t part of the state object itself, Aurelia could include all sub-route configurations at startup (as Angular-UI-Router does) while leaving the sub-states and their templates, dependencies, etc. in separate chunks to be loaded when needed.

5 Likes