Angular routing – advice for real applications

There are plenty of examples and documentation about the Angular router, but these things sometimes leave important questions unaddressed.  Documentation often intentionally demures from questions like “what is the best way to use this?”. Even my own previous post briefly reintroducing the router does the same.

Here are our recommendations from extensive use (at Oasis Digital, in classes and complex customer applications), with my specific take on contentious points, on that category of Routing question. How can the built-in capabilities of Angular, including the router, be used with maximum leverage? How can an application be written “with the grain” of Angular to produce the greatest value with the least code? How can the router be used to provide a good user experience and functionality?

URL/route for navigational state

The standard use of intra-application URLs is to represent and control navigational state. Navigational state means “where” the user is in the application. Which screen; which entity; what they are working on; what they are looking at. This type of state so strongly belongs in the URL that (in a polished, important application) it should always be managed via the router -even if some other state mechanism is being used to manage other aspects of application state.

Pop-ups and auxiliary routes

The Angular router has an auxiliary route feature, uncommon among other routers for other frameworks. This feature has various uses, particularly for (unusual) applications with more than one section of the screen that might be navigated separately. But it also has a common use: if an application has a pop-up/popover/dialogue of some kind (for example, a list of users in which editing a user happens on the same screen), the state of whether a pop-up is currently visible should be represented as an auxiliary route.

Resist the temptation to have a pop-up work separately from the route, because that would mean that bookmarking or sharing a URL would not capture this aspect of the user’s navigational state.

Router state and form state

Sometimes a form is used for data entry; for these cases the state of the form (particularly if you’re using model driven/reactive forms) is a fine place to keep that interim data entry state.

But in other cases, a form is used for something like a faceted search. Search parameters can easily stray into navigational state. For example, if the user is currently searching a list of orders for a certain date range that mention a certain product, they could very reasonably want to navigate forward and back to that state, they could want to bookmark and share the URL, and have those search parameters come along.

In these cases, it is reasonable to mutually interconnect the router state and the state of a form. That sounds difficult, but requires just a few lines of code. The result can easily provide a near ideal user experience around searching, URLs, the back button, bookmarks, and so on.

Router state and ngrx/Store

Ngrx/Store users have some extra tools at their disposal around the router state. There is an optional add-on package which integrates router state into Store state, so that it can be managed via the same mechanisms (actions, reducers, effects, etc. An application of significant complexity, so much so that it needs Store, almost certainly also has significant navigational state, and should strongly consider integrating them together.

Don’t fear ugly URL parameters

In simple cases, a URL contains a flat name-value pair list of optional parameters, in which the contents of such strings are most typically just a single value, it is also acceptable to pack in many values in such a single parameter by encoding a broader swath of state as JSON. For example, consider a simple search of orders in the system for order management. It might have single search parameter, perhaps which matches a product description. The URL for the state of searching for such a description could look like:

/orders/search?productMatch=blue

But for a more complex search (for example think of a faceted search with 15 different fields by which the user could search for old orders), you may need more (bug-hiding) code to shuffle search parameters into and out of URL parameters. It is also acceptable, and sometimes more advisable, to encode all of the complex search parameters like so:

/orders/search?q=...

where … Represents a URL-encoded JSON object describing the search parameters

Such a URL is less straightforward to inspect by hand, but also less work to manipulate programmatically and easier to expand to encompass more parameters. Make the trade-off at the application level, as to whether this yields a better overall system.

Router security concerns

I’ve seen suggestions of route guards is a security mechanism; but it’s important to remember that the entire browser is a user agent, it is literally an agent of the user, not the agent of the developer or of the backend system. At best a browser application can avoid making security worse, but it doesn’t actually provide security. Never assume that route guards or other client-side mechanisms are providing any real security, rather think of these mechanisms as advisory security. Advisory security is UX/UI which makes it easier for the user to avoid wandering into a screen which will break because server-side security rules interfere with its operation.

But there is a new and interesting way that browser-based applications can get things wrong with security where the router is concerned. The entire route URL, which means all route segments, parameters, outlets, etc. is untrusted user input. It could accidentally or intentionally contain errant or malicious data. Make sure to treat route data as such, sanitizing it etc. as one would any other user input.

Matrix parameters

Although not used very widely, there is a URL pattern called a matrix parameter, in which each “segment” of the URL has its own parameters rather than just one single bucket of parameters for the entire URL. The Angular router supports this nicely, by using it you can sometimes conserve application code quite significantly while still providing a more ideal user experience around navigational state captured in the URL.

Route guards for data loading

Longtime Angular users who started with AngularJS often point back to the “route resolve” feature is a critical capability they’re looking for an Angular. The Resolve feature makes it possible to delay (or cancel/fail) loading of a route until the data needed to populate the screen for the route is ready.

I recommend using this feature with caution and sparingly. Often a better user experience can be achieved by proceeding directly to a route (for example, a customer history detail display), and then asynchronously loading various parts of the data which appear on that screen. While the screen painting can be a bit messier this way, the user will perceive that the screen started loading much more quickly than if loading is delayed until all data is available. Even if the difference is only a few hundred milliseconds, showing the user partial results is typically a better default.

Angular routing, a basic Q&A

At Angular Boot Camp, we thoroughly introduce and teach the Angular router – over the course of 3 days, spread out into relevant bits and pieces of other learning. Outside of class though, customers ask a straightforward question: What is the Angular Router, and why should I care?

To answer that, this post is a tidy re-introduction to routing in Angular. It is presented in Q&A form – there is little reason to reproduce the router documentation, so this is more like the average of many conversations.

What is routing?

Most concisely, in a web application routing means the relationship between the URL and the state of the application. State can mean a lot of things, but in this context it means “what screen the user is looking at” and “what specific entity/data the user is looking at on that screen”. For example, an application might have “/orders”in the URL when they are looking at a list of orders, or “/orders/12345” when they are looking at order number 12345.

Why use a router?

Routing is about translating between this concise string in a URL, and the rest of the machinery of an application, without coding that translation “by hand”. Developers sometimes ask why they need a thing called a router to do that, whether they might just instead inspect the “window.location” variable and make the application show the right thing. In a sense, the answer is yes – you could certainly do that. But it tends to get complex as an application grows, and if you do it ad hoc, your code won’t have as much in common with other application code. By using a router, you can write less code, and have a standard off-the-shelf solution to a problem that most applications need to solve.

Why care about the URL?

As you learn Angular, you can see how to use variables and ngIfs to make different data appear on the screen in response to user clicks. For example, you could have a variable “orderScreen” and some section of your template using ngIf to display only if orderScreen==true; then have a button which sets orderScreen=true. So you can easily see how to display different data based on what the user clicks, without caring about the URL.

But URLs (and by this I mean the part after the domain name) are the standard, well proven Web way of expressing “where” the user is. Users understand URLs, and users can copy and paste URLs in email, users can bookmark URLs. If your application has a specific URL to mean “order list screen”, a user could bookmark that and navigate directly to it when they like. Fundamentally, URLs are user-friendly.

Would it be easier to just write a separate “application” for my orders list screen? Could I avoid having to understand the router by making each screen a separate application?

I’ve seen applications, especially those adapted to fit inside an server-side system, which eschew the notion of “routing” between different screens and instead have an entirely separate application for each screen. This is possible, but inefficient. The browser ends up needlessly reloading much of the same JavaScript as the user navigates from one screen to another. With the router, the user will only need to load the new, different code for the next screen as they navigate.

Similarly, the router implements “lazy loading”, so just like the idea of totally separate applications, with the router, a user doesn’t have to wait for their browser to load the “order screen” JavaScript until they are ready to use that screen.

The router provides the ideal mix of efficiency in development, efficiency and deployment.

How do I get started with the Angular router?

As you create a new application with Angular CLI, there is a routing option which sets up the basic structure of routing for you. Unfortunately as of late 2017, you still need to manually code up specific routes, which you can do by following the Angular router documentation or various tutorials online (or of course, learn in our class). I expect a future evolution of the CLI will automate more of the router configuration process.

When should I get started with the Angular router?

A few years ago, I used to recommend waiting for routing until you really need it, until your application has more than one “screen”. But now it seems more advisable to simply follow the standard patterns for routing from the very beginning. As you create your first screen in an Angular application, go ahead and implement that screen in a module, and use router lazy loading the load that one module. This seems like extra structure, but will save you from having to rearrange your application code when a second “screen” is inevitably needed down the road. This is also exactly the path we teach in Angular Boot Camp.

What about that idea of routing to a specific (for example) order, rather than to the list of orders?

The idea of routing to a specific individual entity in your application problem domain, use a route parameter. A route parameter is simply a section of a route which can be filled in at runtime with a string. For example, “/orders/12345” suggests a routing set up where the second segment of the route (12345” is a parameter. This is easily configured in your Angular routing configuration, you can see the documentation for the exact syntax.

The more interesting part of a route parameter is consuming that parameters, being aware of it from inside application code. These route parameters arrive at your application component as an observable value. You’ll need to use a small amount of RxJS code to trigger loading of the appropriate data based on that route parameter. This sounds confusing and complex, but you can find examples online would show it is often just a few lines of code.

How do I link to a route?

You can link to a Angular route in an ordinary anchor element (“<A….”) in a component template. You do this using the router link directive (attribute). The documentation shows the exact syntax, but the important thing here is simply that you link to a route within the same application, with the syntax only mildly different from linking to any other page on the Internet.

Things get slightly more complex when you want to link to a specific entity (going back to our example, “/orders/12345”). To do this you use something called a route parameter array, in which the application code snippet has an array with these two parts of the route (orders, and 12345), which then get assembled automatically by angular routing into a working route link.

Of course in real application, users often click a button to do something rather than follow an ordinary web link; you can accommodate this with routing either by styling the link to look like a button (quite easy with bootstrap, for example) or with a line of code in a click handler to ask the router to navigate to a link.

So my links could be navigation in a sidebar or top bar, right?

Yes, the most common use for router links is in a navigation bar of some kind.

In this context, it also makes sense to visually mark which route link is currently “active”. To make it obvious to the user which part of the application they have already navigated to. The Angular router also makes this quite easy with an attribute “routerLinkActive”.

Is there anything else to know about routing?

There is an abundance of important capabilities in the router beyond this quick Q&A introduction. Past this introduction though, it starts to get a little bit more philosophical, and make sense to study after you are already experienced with basic use of the Angular router.  I will follow up with another post on some of these other routing thoughts.