The 7-step process of Angular router navigation

The 7-step process of Angular router navigation
Photo by Alexander Andrews / Unsplash

There are many great articles and books available on how to use and configure Angular router. In contrast, little information is available on what Angular router actually does once it is running.

This article is not an introduction to Angular router or a guide on how to configure it. Instead, this article focuses on what Angular router does at runtime, after the router configuration has been loaded.

If you are not yet familiar with Angular router, I highly recommend you first read this introduction to routing on SitePoint, the official routing guide or Victor Savkin's book "Angular Router" (if you are interested in buying the book, there is link to get 50% off at the bottom of this article).

By the end of this article, you will understand the 7-step process that Angular router goes through when a user navigates from one page to another.

So let's get started!

The 7-step routing process

Every time a link is clicked or the browser URL changes, Angular router makes sure your application reacts accordingly.

To accomplish that, Angular router performs the following 7 steps in order:

  1. Parse: it parses the browser URL the user wants to navigate to
  2. Redirect: it applies a URL redirect (if one is defined)
  3. Identify: it identifies which router state corresponds to the URL
  4. Guard: it runs the guards that are defined in the router state
  5. Resolve: it resolves the required data for the router state
  6. Activate: it activates the Angular components to display the page
  7. Manage: it manages navigation and repeats the process when a new URL is requested

To remember the 7 steps, you can use the mnemonic PRIGRAM, where each letter represents a step in the routing process:

  • Parse
  • Redirect
  • Identify
  • Guard
  • Resolve
  • Activate
  • Manage

Before we dive in each of the steps, let's recap the terms that Angular router uses.

Terminology

  • router service: the global Angular router service in our application
  • router configuration: definition of all possible router states our application can be in
  • router state: a state of the router at some point in time, expressed as a tree of activated route snapshots
  • activated route snapshot: provides access to the URL, parameters and data for a router state node
  • guard: script that runs when a route is loaded, activated or deactivated
  • resolver: script that fetches data before the requested page is activated
  • router outlet: location in the DOM where Angular router can place activated components
  • URL segments: parts of the URL that are divided by slashes

If you are not yet familiar with Angular router or any these terms sound unfamiliar, I highly recommend you first read this introduction to routing on SitePoint, the official routing guide or Victor Savkin's book "Angular Router" (if you are interested in buying the book, there is link to get 50% off at the bottom of this article).

Step 1 - Parse the browser URL

As far as Angular router is concerned, the browser URL is a string that represents a router state. And because a router state is a tree, Angular router essentially considers the browser URL a serialized tree.

In step 1 of the routing process, Angular router takes the browser URL and parses it as a URL tree.

A URL tree is a data structure that will later help Angular router identify the router state tree in step 3.

To parse the URL, Angular uses the following conventions:

For example, when parsing the following URL:

/section-one;test=one/(nav:navigation;test=two//main:about;test=three)?query=four#frag

Angular identifies the following parts (click here for a bigger version):

  • section-one, navigation and about are URL segments
  • ;test=one, ;test=two and ;test=three are matrix parameters
  • (nav:navigation;test=two) is a secondary route that specifies URL segment navigation for an outlet named nav and assigns its own matrix parameter that has a string value of two
  • (main:about;test=three) is a secondary route that specifies URL segment about for an outlet named main and assigns its own matrix parameter test that has a string value of three.
  • // is used to separate both secondary routes
  • ?query=four assigns a query string variable query with a string value of four
  • #frag assigns the fragment frag

Matrix parameters are scoped on a route level. Different routes can have matrix parameters with the same name and different values.

In contrast, query string parameters and the fragment are not scoped on a route level. They are shared across routes and thus their names should be unique.

Once Angular router has assembled the URL tree, it continues to step 2.

Step 2 - Redirect

Before Angular router uses the URL tree to create a router state, it checks to see if any redirects should be applied.

There are 2 kinds of redirects:

  • local redirect:
  • when redirectTo does not start with a slash
  • replaces a single URL segment
  • Example: { path: 'one', redirectTo: 'two' }
  • absolute redirect:
  • when redirectTo starts with a slash
  • replaces the entire URL
  • Example: { path: 'one', redirectTo: '/two' }

Angular router walks through the router configuration. As soon as it finds a matching redirect, the redirect is applied and the router continues to step 3.

Only one redirect is applied!

If route 1 redirects to route 2, which in turn redirects to route 3, then the second redirect to route 3 is not performed and route 2 is activated.

Step 3 - Identify the router state

At this point, Angular router has a URL tree with a potential redirect applied.

Angular router traverses the URL tree and matches the URL segments against the paths configured in the router configuration.

If a URL segment matches the path of a route, the route's child routes are matched against the remaining URL segments until all URL segments are matched.

If no complete match is found, the router backtracks to find a match in the next sibling route.

Consider the following router configuration:

[
  {
    path: 'one',
    component: OneComponent,
    children: [
      {
        path: 'two/three',
        component: OtherComponent
    ]
  }
]

and the following URL:

/one/two/three

then Angular router will find a match that consists of two routes:

  • route with path one that matches one URL segment
  • child route with path two/three that matches two URL segments

As soon as Angular router finds a complete match that consumes all URL segments, the router state is constructed and the router continues to step 4.

Notice that Angular router has no notion of route precision. As soon as a complete match is found, Angular router stops processing the configuration. Therefore it is important to make sure that your routes are configured in the correct order. If you add a wildcard route as the first route, no other routes would be reached and the wildcard route would always be matched. As a result, you should always add a wildcard route as the last route in your router configuration.

If the entire router configuration is processed and there is no match, router navigation fails and an error is logged.

Step 4 - Guard - run guards

Now that Angular router knows which router state to navigate to, it runs the associated guards to check whether navigation to the new router state is allowed.

First, it runs the following guards from the deepest child route to the top:

  • CanDeactivate
  • CanActivateChild

Then it runs the following guard from the top route to the deepest child route:

  • CanActivate

If the new router state requires a module to be lazy loaded, the following guard is also run:

  • CanLoad

A guard must return a boolean or a promise/observable that resolves to a boolean value.

As soon as any guard returns a falsy value, the navigation is cancelled.

If none of the guards return a false value, Angular router continues to step 5.

Step 5 - Resolve - run resolvers

Because Angular router knows that the new router state can be activated, it runs the associated resolvers.

During configuration, you can attach static data to a route using the route's data property:

{
  path: 'one',
  component: OneComponent,
  data: {
    name: 'Jazz'
  }
}

Resolvers allow you to dynamically resolve data at runtime. The newly resolved data is then merged into the existing static data in the data property:

{
  path: 'one',
  component: OneComponent,
  data: {
    name: 'Jazz'
  },
  resolve: {

    // Return value of AddressResolver will be merged in data
    // and will be available as data.address
    address: AddressResolver
}

A resolver is a function or a class with a resolve method that returns a value, a promise or an observable. If a resolver returns a promise or an observable, Angular router waits until it completes before it continues to step 6.

Once all resolvers have completed, their return values are merged in the route's data property and Angular router continues to step 6.

Step 6 - Activate components

At this point, Angular router instantiates the required components and places them right next to the corresponding <router-outlet> elements in the DOM.

If a component was already instantiated in the previous router state and only route parameters changed, the component is not re-instantiated but reactivated and the new route parameters are available via an observable in ActivatedRoute.

When all components are instantiated or reactivated, Angular router updates the URL in the browser's URL.

Step 7 - Manage navigation

Finally, when the new router state has been displayed to the screen, Angular router listens for URL changes and state changes.

As soon as one of the following happens:

  • the user changes the browser URL
  • the user clicks a link (using routerLink)
  • an imperative navigation command is performed (using router.navigate)

Angular router repeats the entire process.

Summary

In this article we learned what Angular router does when the user navigates from one page to another.

You can use the mnemonic PRIGRAM:

  • Parse
  • Redirect
  • Identify
  • Guard
  • Resolve
  • Activate
  • Manage

to remember the order of the steps that Angular router goes through.

Knowing this process will help you better understand what happens behind the scenes and help you debug potential routing issues.

And next time someone asks you whether a guard runs before or after a resolver, you'll know exactly what to say.

Have a great one!

Get 50% off Victor Savkin's book Angular Router

This article is heavily inspired by Victor Savkin's book "Angular Router". If you are interested in learning more about Angular router, I highly recommend reading the book.

Victor was generous enough to offer a 50% discount to all readers of this article. Click here to get your copy at 50% off. Thank you, Victor!

PS: I am not affiliated with Victor Savkin nor am I paid to include any links in this article. The discount is a unique gesture of Victor to all readers of jvandemo.com. So grab it while you can!