In this post, we are going to do a guided tour of the main routing configuration concepts needed to use the Angular Router effectively.
The goal is to get a solid initial understanding of the Angular Router, before presenting a more advanced example. This post is the first of a series on the Angular Router, here is the complete series:
Table Of Contents
In this post, we will cover the following topics:
- Initial Router Setup and configuration, avoiding a usual setup pitfall
- Setup a home route and a fallback route, learn why order matters
- Router navigation using routerLink
- Master-Detail with Child Routes - how do Child Routes work?
- The notion of Route Snapshot and Router Snapshot
- Auxiliary Routes: what are they and when are they useful?
Angular Router Configuration: an Introduction
The Angular Router is what makes an Angular Application a Single Page Application, or SPA. For learning all about the benefits of the SPA architecture, have a look at this post.
The first thing that we should do is simply write some routing configuration. What we are doing in this initial configuration is to map certain URL paths to Angular components: meaning that if there is a path match then the component gets displayed:
This configuration means:
- if you navigate to
Homecomponent gets displayed
- if you navigate to
AllLessonscomponent gets displayed
- and if you navigate elsewhere you are going to get an error
But where do these components get displayed?
Configuring a primary router outlet
Once the router has a URL match, it will try to display the corresponding matching components. for that it will look in the template for a
Router outlet is a dynamic component that the router uses to display in this case either the
Home or the
AllLessons components. There is nothing special about these components, these could be any component.
Bootstrapping the router
To finish the setup of the router, we also need to add its directives and injectables into the Angular bootstrap system. We do so by importing the
RouterModule into the application root module:
Notice that we configure the module by using the
forRoot function instead of simply adding the
RouterModule. To learn more about why this is necessary, have a look at this other post on @NgModule.
With this, if we access the
/lessons URLs, we would get the corresponding component displayed.
But here is where for new users of the router things might start to go wrong.
What could go wrong so soon?
With the current setup, if we navigate to an URL from the index page using the router built-in mechanisms, it all works. but if we try to type the URL in the browser address bar to access directly for example
/lessons, we get a 404 page not found error. Why is that?
Understanding the 404 Not Found navigation error
The first thing to know about the new router is that by default it uses the HTML5 History API. This means that routing is not based anymore on using the
# part of the URL, which is used to link directly to a section of the page.
#part (known as the fragment) of the url is a link to a given section inside the page. Changing this does NOT cause a full page reload
This means that when the router navigates to
/lessons, that URL is really shown in the browser address bar. This is opposed to the ancient routing were the browser address bar would display
Why this problem does not occur using hash navigation
In the ancient strategy the URL was pointing to the root of the domain, so when hitting refresh this would reload
index.html, our single page application.
This is because the
# fragment in the URL is ignored by the server - this information is only used by the browser.
But now in the new HTML History API strategy, this will cause an attempt to load a file called
lessons, which does not exist, and so we get 404 Not found.
How to prevent the 404 not found issue?
In order to use the new HTLM5 strategy, you need to setup your server so that any unmatched request gets directed to
index.html, so that for example
/lessons gets as result
index.html and not 404 Not found.
The exact configuration will depend on which server technology is being used. Let's give an example, let's say we are using Node for the server and Express as the backend web framework.
To fix the 404 issue we would have to configure the following middleware as the last in the middleware chain:
This setup would give us a good start for using the new HTML5 mode of the router, and it's important to get this right from the start.
Another thing that probably all applications need is configure a default or a fallback route.
Home and Fallback routes - why order matters
The new component router supports the notions of empty paths and wildcards, meaning that we can configure an index route and a fallback route like this:
We can see that the empty path configuration would map the URL
/ to the component
Home, and all other paths to
PageNotFoundComponent. But there is a catch in this configuration as well.
Why order matters
One of the key things to know about routing configuration is that the order matters a lot. When the router receives an URL, it will start going through the configuration in order: starting with the first element of the configuration array.
If it finds a match to the complete URL, it stops and instantiates the corresponding component(s). So in this case, if we would put the fallback configuration in the beginning of the array, every URL will match to the
** wildcard and this break routing.
That's why we should put the fallback route configuration as the last entry in the array. With this baseline configuration, let's now set up some router navigation. There are two ways of doing this:
- declarative template based navigation with the
- programmatic or imperative navigation with the
Router Navigation with the routerLink directive
As we have included
RouterModule in our app, we can use the
routerLink directive to define router navigation links in our template. There are a couple of ways of doing this:
We can either hardcode a string directly in the template, like its the case of the home route or the courses route. But we can also pass it an expression. If so we need to pass it an array containing the multiple URL path parts that we want to navigate to: in this case we want to navigate to the
Programmatic router navigation
Another way of doing router navigation is to use the router programmatic API to do so. For that we just have to inject the router into our component, and make use of either the
navigateByUrl navigation methods:
One of the things that we usually want to do when navigating between two routes is to pass navigation parameters to the target route.
Route Parameters - Avoid Memory Leaks
If we want to read parameters between routes, we probably want to use the route parameters observable that the Router API exposes. For example, when navigating to the course detail component using
/courses/1 (1 being the course Id), we can recover the course Id from the URL:
As we can see the router provides observables that allow us to observe routing changes and react to them. One important pitfall to avoid with router navigation is to prevent memory leaks.
Have a look at the lesson Exiting an Angular Route - How To Prevent Memory Leaks for some more details.
The notion of Route Snapshot and Router Snapshot
One key notion about the new component router is that it's reactive. This means its API exposes several observables that can be subscribed to react to routing changes.
But sometimes we don't want the latest value of a route or its parameters, we just the values that were present only at the moment when the component was initially instantiated, and we usually want those values synchronously and not asynchronously.
For that, the reactive router introduces also the notion of snapshot. A snapshot can be injected in the constructor of the routed component:
With these snapshots we have access to the route parameters at the moment of navigation.
Why do we a need a snapshot of the whole router?
We can access the snapshot of the current route, but also of the whole router. The snapshot of the whole router would be useful for accessing for example route parameters of parent routes, like in the example above.
And this is just one of the many new features of the new router. The new router also implements the usual features of routing, such as Child routes which allow us to implement common UI patterns like Master Detail.
Implement Master Detail using Child Routes
Actually we were already using at least the notion of child routes without even knowing. When a route has multiple child routes, only one of those child routes can be active at any given time.
This sounds a lot like what was happening with our top-level routing configuration. Only
/lessons can be active at any given time. In fact, using the empty path routing feature and making the top-level route a componentless route, we can rewrite the configuration using child routes:
This gives the exact same result as before: only
/lessons can be active at one given time. Remember all this route config only has one
router-outlet, so the end result of the matching must be a single component and not multiple.
Also note that a componentless route is a route that participates in the path matching process but does not trigger the instantiation of a route component.
Using Child Routes for implementing Master Detail
One use case of child routes is to use them to implement the common UI pattern known as Master Detail. Actually we are going to go one step further and implement a master route with multiple types of detail routes.
Imagine a course with a list of lessons. You click on a lesson in the list in order to display it. But there is a catch, there are multiple types of lessons: video lessons, text lectures, quizzes or interactive exercises.
One way to configure this would be to use child routes:
This is one way to do it, we have gone here into several levels of nesting to show that it's possible.
The way this works is that when the user clicks in one of the lessons, depending on the link clicked a new detail screen will show replacing the master
This is the basis of child routes which is a common feature in many routers. Another common feature that is sometimes not used to its full potential are auxiliary routes.
Auxiliary Routes: what are they and when are they useful?
First, what are auxiliary routes? These are just plain routes like the primary route that was mapped to the
router-outlet component. But instead, auxiliary routes are mapped to a different outlet which must be named (unlike the primary outlet).
This is an example of a page with multiple outlets, each corresponding to a subset of routing configuration:
But how can this work, because all matching is done using the URL, and there is only one URL. Right?
Multiple outlets, but only one URL?
The key thing to realize about top-level auxiliary routes is that effectively each one has its own URL to match to, starting at
/. Auxiliary routes can also be configured not at the top-level, but let's focus on that scenario in this post.
Imagine that you divide your browser window into multiple mini-browser windows, each with its own separate URL. Then you provide separate routing configuration for each of those windows because you would want those windows to be navigated separately. Here are some examples.
Practical use cases of auxiliary routes
As you can see, different outlets correspond to different auxiliary routes. But when would you want to use auxiliary routes and why?
It's very common for applications to divide the page into multiple regions:
- the top-level menu
- the side menu that often is a subsection of the top menu
- an aside on the right maybe displaying a playlist of lessons
- popup dialogs for editing a list detail, that you want to keep upon during navigation
a chat window that stays opened during navigation
An example of an auxiliary route configuration
Imagine that to the right of our screen, we want to add a playlist of lessons that gets different content when we navigate: It might contain the list of latest lessons, or the lessons of a given course:
What we have configured here is that when the path of the
aside outlet is set to
playlist, we are going to display the component
Playlist. This routing can be defined independently of the primary route.
Let's see how does this work, how can the URL be used to display two URLs instead of one?
What does the URL look like for accessing an auxiliary route?
The Angular Router introduces a special syntax to allow to define auxiliary route URLs in the same URL as the primary route URL. Let's say that we want to trigger navigation and show
AllLessons in the primary outlet and
Playlist in the
rightAside outlet. The URL would look like this:
We can see that
/lessons would still route the primary route to the
AllLessons component. But inside parentheses we have an auxiliary route. First, we have the name of the outlet to which it refers to:
Then we have a colon separator and then we have the url that we want to apply to that outlet, in this case
/playlist. This would cause the
Playlist component to show in place of the
Note that you could have multiple auxiliary routes inside parenthesis, separated by
//. for example this would define the url for a left-menu outlet:
The Angular Router is packed with super useful features. In this post we have gone over some of its core concepts with examples: initial setup while avoiding pitfalls, router navigation, child routes and auxiliary routes.
With just a handful of concepts, we can configure all sorts of different routing scenarios.
With a solid grasp on the router fundamentals, let's now go over a more advanced example in the next post of this series: Angular Router: A Complete Example (using Bootstrap)
I hope that this post helps with getting started with the Angular Router and that you enjoyed it!
If you have some questions or comments please let me know in the comments below and I will get back to you.
To get notified of upcoming posts on the Angular Router and other Angular topics, I invite you to subscribe to our newsletter:
Router Lessons available on YouTube
Do you like learning on YouTube? There are several router lessons on the playlist bellow, including router setup, child routes and auxiliary routes. Subscribe for more lessons, I hope you find the lessons useful:
Other posts on Angular
If you enjoyed this post, here some other popular posts in this blog:
- Angular Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular Components - The Fundamentals
- How to run Angular in Production Today
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven, Model Driven or In-Between
- Angular ngFor - Learn all Features including trackBy, why is it not only for Arrays?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
From the Victor Savkin blog (@victorsavkin):