What the Fudge Is a Route Resolver in Angular?
By: Aditya Mahapatra
Tags: Angular

Great caption yo!

With Lighthouse out on the loose, you better keep your load time on the clock! When your feature modules are thick, juicy (you know what I mean) and eagerly loaded, your initial load time will be off the charts and that would make the Lighthouse very angry! But to tackle this, you can lazy load your modules and keep your load time on the budget. From there, you can progressively load your modules and make your application slicker without cutting corners. Remember when I say this, you should never make a user wait longer than three to four (these number are totally fictitious) turns of your progress spinner.

Here’s the thing, you have lazy-loaded all your feature modules, but one of them is a big boy and have a lot to deliver. The load time for that module will in turn be more and this will mess up the user experience again. Don’t worry, Angular provides another solution for this. You can preload your modules in the background and this will solve your problem again. But now you want to go an extra mile and load each component’s data first and then display the entire page. For this, you can use the route resolvers. Resolvers block the entire page until all the data is available to fully display it. Alright, enough theory. Let’s now dig into the code and see, what differences does route resolvers bring to the table.

To get you started, I have already prepared a little starter project, which you can use to move forward with this tutorial. Here’s the git link. Just for your heads up, in this starter app, I have created two feature modules, todos and albums . In each module, I am making an http call to the backend and fetching a bunch of albums a todos. Then I am displaying those albums and todos as a list in my template. I have added a bit of CSS styling, so that the template would not look like a hideous barebone. In the AppRoutingModule, you will find that, I am lazy loading both the feature modules and I have set my prealodingStrategy as preloadAllModules, so that my feature modules will be preloaded in the background. Alright, enough talk! After installing the dependencies, type ng s -o on your CLI. This would open the starter app on port 4200 in your default browser. But, when you open the console on the browser, you will get a big fat error, something like below.

I have just had it the errors!

When you navigate to Albums, you will get the same error as well! Don’t worry, this error is a part of my sales pitch and the basis of the very premise which brought you and me together for a hefty discussion. Now let’s take a look at what may be the cause of the error.

Have a look at the image!

When the component is initialized, I am making a server call and fetching the data. Then I am assigning the data to the todos array. Then, I am displaying the todos array as a list in the template. But for the first few milliseconds, when the data is being fetched, the todos array is undefined. So, in the template, when I am trying to access todos.length , I am getting the error.

Well now, the first thought in your mind would be that I am a dimwit. I agree, you can do a null check and that would most certainly solve your problem. But take a pause from dissing me for a moment and imagine your template is a thousand lines of conditionally rendered div soup. And you realize it halfway through that you have forgotten to do all the null checks. Boy oh boy! Wouldn’t that be a disaster, revisiting a bunch of html just to make petty null checks. I don’t know about you, but that sure would be a headache for me. So, to do damage control, I would opt out for the only feasible solution, that is defining a route resolver for that feature module. Let’s see how can we do that. Type the below command in the terminal.

ng g s todos/todo-resolver

The definition of Resolve interface

A route resolver is nothing but a service which implements the Resolve interface from @angular/router . The Resolve interface in turn has a method resolve which returns an Observable<T>, Promise<T> or <T>. The resolve method takes two parameter which gives us extra information about the route itself, for example the route parameters. Now let’s look at how can we implement the route resolver for the todos feature module. Navigate to todos/todo-resolver.service.ts and make make necessary changes as per the below code.

In the above code, you can see our TodoResolverService class implements the Resolve interface. We also define the resolve method which returns the data of type ITodo[]. I just wanted to show you few subtleties about resolvers. So to start off, we can return a bunch of static data and then try returning an observable and a promise. Now let’s wire up our AppRoutingModule and TodosComponent, so that the data from the resolver will be loaded first, and then the page will be displayed, thereby getting rid of that hideous error.

In the app-routing.module.ts, we will have to add a resolve object to our todos route path, something like resolve: {todos: TodoResolverService} . For this of course, we will have to import the TodoResolverService. Change your code in app-routing.module.ts accordingly.

In the component, we can get the data from the resolver from the ActivatedRoute instance. We will have to import the ActivatedRoute from @angular/router and then inject it to get an instance of it. Then inside ngOnInit, we can subscribe to this.activateRouteInstance.data to get the data from the resolver before the page is displayed. Refer from line number 16 to 20 of the above gist and make necessary changes in your TodosComponent.

Alright, now that everything’s wired up, check if you get that error anymore.

Tadow! There’s no more error, it means that we have successfully loaded the resolver data before displaying the page. Okay, now that we have seen, the resolver works fine when we are returning static data, let’s try and return an observable from our resolver.

In stead of returning the static data, let’s try wrapping it in an Observable and return that. Refer from line number 36 to 40. But now, if you go to the browser, you will find that your todos route will be totally broken. Why did that happen?

Remember, whenever we are returning an observable from our route resolver, the observable needs to be complete. Otherwise the route will not work. Same goes for Promise as well. The promise needs to be resolved before being returned.

After adding the line observer.complete() you will see that our resolver is back in action and our todos route is working properly again.

Okay now we are ready to make the server call in our resolver. So let’s do that and check everything’s working fine!

Call the getTodos() method from the TodoService. That would return an observable of type ITodo[]. We can return the same observable from our route resolver. Let’s see if this works fine or not!

There you go! We have successfully configured a route resolver for our todos route and we are loading the data from the backend first and then displaying our list.

Quick note though, whenever you have setup both route guard and route resolver for a particular route, the route guard will always have higher priority than the route resolver. It means that the resolvers will not be executed until all the guards have run and succeeded.

Alright, I am sure that, by now you must have a basic understanding of how route resolvers work in Angular. In order to get a hang of it, I suggest you to configure a resolver for the albums route as well. I also suggest you to try having a route guard along with a route resolver and see which one executes first.

If you get stuck at any point you can always refer to the completed project.

Also if you pay a bit of extra attention while clicking the todos and albums buttons, you can see that the pages are not rendered instantaneously. It takes a fraction of second and then navigate to that route. Well do let me know if you can spot that or not in your wishful comments!