Angular2 router’s bumpy ride – a user’s perspective

Edit: ui-router is not mentioned at all in this article and should have been. This is only due to the fact that I haven’t worked with it enough to comment on it, and also because it is still in alpha, and not a popular ng2 router alternative in the community yet. That being said, it is the “only” router for me when in ng1-land, so I’m eager to give it a try, or see how much of it’s architecture influences Vladivostok.

Angular 2 is now in a release candidate state, after several beta releases, and while the core of this new iteration is an extremely solid one, many of its components are still under heavy development, which makes using them quite a bumpy ride.

The router component is perhaps the most notorious among them, with two iterations deprecated in the pace of a few short months – one officially so, and one never really seeing the light of day – and a third one on the way.

Now, it needs to be said that creating something like a router is far from trivial, particularly so if you are setting out to “revolutionise”, meaning solve all the known problems of previous routers. In the case of routing these are lazy loading, handling complex route structures, and enough flexibly to account for all use cases (with more or less legwork required).

Also, the reason why the angular team has gone through so many iterations has to do with how closely they are working with the community of users – the current iteration having taken a mere couple of months to get thrown out, so quick the community was to spot its shortcomings.

So, how do all of these routers differ, and where are they headed?

Enter @angular/router-deprecated

Angular2’s first stab at a router relied on the component paradigm heavily, as does angular2 in general. Components may have @RouteConfig annotations with route lists defined, and if they do, these get parsed and the relevant components loaded into a node in its template.

Most lifecycle hooks and checks could then be in the component itself, keeping thing neat and clean. This approach had a couple of problems:

  • As Routes were defined in the class file, deep linking to unloaded classes is impossible.
  • @CanActivate, which determines whether or not a certain route could be activated, had to be an annotation as it ran before the Component itself was instantiated.
  • Routes followed the same isolation pattern that Components did, but this meant not having access to the full route tree at any point, and having to hack your way around everything.

Enter @angular/router(-deprecated?)

The first attempt to solve these issues was promising:

  • It solved the deep linking problem by having routes be directly inferable from the url.
  • It intended to replace @CanActivate with CanActivateChild – it is now the parent’s task to determine if the route activation process can continue.
  • Access to the whole tree was given at any hooks

Unfortunately, it perpetuated some of the issues, like routes still defined as a Component’s annotation, and its development didn’t get very far before it getting scrapped – first unofficially and now officially so.

Enter @ngrx/router, and the “new new new Router”

If “new new new Router” seems like an atrocious expression it’s because it is – but it’s been a recurrent one in places like Gitter or Github issues. It is Vladivostok, and it’s approach is very similar to @ngrx/router (as its devs have been collaborating with the angular team closely).

@ngrx/router takes a cleaner, leaner and more low lever approach to routing:

  • Routes are defined as objects in their own right and injected into the app directly. Their loading becomes completely independent from the Components themselves.
  • A route has Guards that run whenever the route tree passes through it, these again completely independent from which Component is actually being loaded.
  • Changes in url that do not actually change routes, but only parameters (like changing from /user/1 to /user/2, for instance) do nothing by default – it is the user’s responsibility to listen to these changes and trigger behaviour
  • Routes, RouteParams, QueryParams, RouteData… All these are Observables that any Component can listen to – this makes it both more flexible and simpler, specially when creating something like a breadcrumb component, or anything more specific or unique.

A conclusion of sorts

Angular2 is heading in a really good direction, despite (or perhaps because of) all the growing pains it is going through. The downside of this is that it can’t live up to the extremely high expectations for everything from power to speed to ease of use, while in its betas and RCs.

The best way to get ready for the new router is to delve into @ngrx/router, which coincidentally is a pretty powerful tool in its own right. The documentation is sparse but its developers and users are quick to answer in their Gitter channel, and it is flexible enough to handle almost anything you’ll want to throw at it.

I’ll be throwing a couple of things myself, and write about that next.

3 Comments

  1. I’ve been digging into the routing for over a month now and I have just recently switch over from trying to work with the Component Router to the ngRx Router. The ngRx Router was immediately easier to work with and solved problems that Component Router didn’t seem to handle at all. That said, I’m also finding that many of the conveniences of the ng 1.x $location service are severely lacking in any of the routers. Specifically, the ability to change .search() parameters independently. All the new generation routers take such an all-or-nothing approach. It’s frustrating.

    1. Ben, we definitely feel the frustration too. I have to agree a part of this is the very strong opinions that has driven the design and features of the routers so that they do end up feeling like all-or-nothing as you say. But another large part of our frustration was that we started with angular 2 quite early and had to go through the entire journey with them. I think with each iteration of the angular router, and even switching to ngrx router and back, we were hoping we would solve all our use cases without the hacks we put in along the way but each fell short in different ways. I suppose it isn’t immediately clear from what Tiago wrote here that he actually took our product through each of these iterations of the router over the past few months! And just in the past few days, he has moved our code once again to what is hopefully the final angular 2 router. I think even with the latest, we had to deal with not having access to the full routing tree but the way we did it this time is less hacky than with the original Component Router. But we’re settling there. I think Tiago is planning on a follow up post on this after he’s back from his vacation. By the way, both Tiago and I have found your posts very helpful when we were left scratching our heads so just wanted to say a quick thanks for the work you do.

  2. One of the things that I’m thinking about doing is creating a proxy component that essentially subscribes to all the router events and then maintains a Synchronous internal state of the router. Then, I can use this proxy object to do more “relative” style navigation. We’ll see – this R&D has taken much longer than I had anticipated in the first place 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *