Angular Router Log

Nikolaos Margaris
5 min readMar 3, 2021

A while ago, a colleague of mine asked me:

Hey! do you know an efficient way of displaying Angular router events?

What for?” … I replied.

And the answer I got, was:

I need to debug a problem during which, browser address bar is not in sync with my filtered list".

Hmm… I sat down for a while and thought about it and so… in the end, it seemed like a nice idea for a small side project. After all, we will end up applying several development process related concepts and also embrace clean code and functional programming.

Angular Router

Like every framework that “respects” developers, so does Angular by providing routing capabilities i.e seamless in-app navigation through several views for Single Page Applications via @angular/router. This is mainly done by allowing developers to configure a list of routes (views) along with their respective components (controllers) and of course their lazy-loading and guarding options.

Angular Router Log

The main idea for this project is to be able to monitor router event changes flow while changing routes either via links or navigating via browser history buttons. Yes, you could just enableTracing while defining forRoot configuration, however wouldn’t it be better to display router events in a cleaner way.

So, let’s start by creating the project:

ng new router-log

Project should now be ready to run:

cd router-log
npm start

What we need now is to create a couple of lazy loaded modules:

ng g module todos --route todos --module app.module

and

ng g module broken --route broken --module app.module

We are going to leave the default views intact, however we will add a guard that blocks one of them (for demonstration purposes).

So we create the guard:

ng g guard block --implements CanActivate

And we change its return value to “false” (i.e always block):

Last but not least, we make sure that this guard is being used at “broken” lazy-loaded module:

Design Review

Before working on our main functionality, it would be professional to do a small design review and decide on the basic concept, along with its key parts.

So after heavy thoughts, we decided that we:

  • Need to listen for route changes
  • Need to group several events that might be emitted during a single route change
  • Need to be type safe and define proper models
  • Need to decide on the UX
  • Need to test our solution

Main functionality

Subscribing to events initially (please bare with me) would be something like this:

And then we need to group router logs by using NavigationStart and NavigationEnd as initial and final events of the same group.

But, how are we going to do that?

Well, now it would be a good time to look for a proper RX operator that would help us recursively gather all those events to a single array and then publish it forward. It looks like there are two operators for this job, “reduce" and “scan". The main difference between those is that “reduce” works like fold on every value emitted and expects stream to finalize, whereas “scan" applies a function using the previous emitted value and the current one.

Since source stream (i.e router events) is not expected to finalize, then we will use “scan" operator. So whenever a NavigationStart event is emitted, we will be creating a new log-group and on every subsequent event we will be adding it to the already created log-group.

There is also a final operation that we need to do and that is to

allow only the final log-group to pass through the stream

And that is because “scan" allows every function application’s result to be emitted forward to the stream and we only need the group of logs to be emitted as a whole. Explaining things better, we need to proceed as following:

  • subscribe to events
  • map to a possible new object
  • scan previous values (like we discussed) and combine them with current one
  • filter our intermediate scanned results and accept only the last one when log-group is ready (we will see what I mean by this)

Models, models & models

Moving further with our implementation, it is crucial at the moment to increase our confidence on coding and also further expanding or refactoring our current app solution.

We use Typescript, so we will spend as much time as possible in order to properly

Define Types for our data structures, our models and our objects.

If we fail at this stage, then perhaps we will possibly face a lot of problems while debugging our app and in the end quitting with our efforts.

So here it is:

Note: check Factory method pattern

Final Implementation

A lot of is happening here, so please spend some time inspecting the result!

Good!

Now, let’s improve it ;)

First some utilities:

And now, functional composition applied at some part of the code:

Check out “filter-operator” functionality and how utility functions are invoked in sequence in order to get final result.

Ah! The benefits of Functional Programming! Isn’t that great!

Utility pure functions can be tested, reused, and guarantee to always return the same result. The latter implies that memoization techniques could also be used. Finally, please note that “scan-operator” functionality could be improved, however comments were chosen instead for clarity’s sake.

Last but not least, we need to display logs. So…

Visibility of Things :D

A lot of UI or CSS frameworks/libraries could be used in order to help us with the presentation of our final solution, however we decided that we need the lightest possible one and the easiest to setup, when it comes to collapsible lists.

We had to choose between several, greatly used and appreciated by the community, ones:

However we ended up using mini-css, because of its simplicity.

Check out the result template:

Screenshots

This is how our final project looks like.

And after a couple of clicks on either the “Todos” or “Broken” navigation links, or browser back/next buttons we get

Final Thoughts & Further Improvements

I always say:

Nothing is perfect, and code is agile too.

So there would always be something to improve:

  • we could create smaller presentational components i.e <log-group …>,<log …>
  • we should definitely test the utility functions
  • we could transfer the event-handling functionality into an Angular directive, which has access both to DOM and view (check also separation concerns)

And, That is all!

Thank you for your patience!

Also, thanks to my colleague for the idea.

--

--

Nikolaos Margaris

FrontEnd Technical Lead, all-around player, experienced but also out-of-the-box thinker. ‘New’ has always something to give and ‘old’ always provides the basis.