Angular Router Tutorial: Setting Up Routing in Your Application

by | Apr 18, 2018

In my previous post I discussed application structure using Angular modules, and set up the groundwork for using core, shared and feature modules. In this post I will demonstrate how to setup basic routing in an Angular application using Angular Router Modules.

**If you are following along with the tutorial, I have added bootstrap to the application. I have written about adding bootstrap to an angular application here: Using Bootstrap 4 with Angular

**If you are not following along, I currently have an generated application with a core module added in which we are going to place the rest of our application content.

Before we jump in, here are some links to other Angular Tutorials I’ve put together:

 

So, let’s get started with this Angular Router tutorial.

 

Generate Components

First we are going to need some additional components in the application. Let’s add a dashboard, sidebar and banner:

  • ng g component core/dashboard
  • ng g component core/sidebar
  • ng g component core/banner

These components should be added under the core/ folder and declared in our CoreModule. Since these are root components for our CoreModule we don’t need to create a feature module for them. Next we are going to set up our application routing, we will come back to these components shortly.

 

Create and Setup AppRoutingModule

The first thing we will have to do is add an AppRoutingModule to define our root routes. In a previous post we had placed our core component selector in our app.component.html. Instead of using the selector, we will setup routing and use routing to display our CoreComponent in app.component.html using a RouterOutlet

Angular.io documentation defines a RouterOutlet as “a placeholder that Angular dynamically fills based on the current router state”. https://angular.io/api/router/RouterOutlet

To do so, do the following:

  1. If your are following along from previous posts, replace <app-core></app-core> with <router-outlet></router-outlet> inside app.component.html. Otherwise replace the content in app.component.html with <router-outlet></router-outlet>.
  2. Create a file called app-routing.module.ts at the same level as the AppComponent
    • In the future, when you create a feature module that is going to have routing. Use a --routing flag with the generate command and this file will be created for you.
  3. Add the following code to app-routing.modules.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CoreComponent } from './core/core.component';
const routes: Routes = [
  {
    path: '',
    component: CoreComponent
  }, {
    path: '**',
    component: CoreComponent
  }
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

4. Add the new AppRoutingModule to the imports array in AppModule

If you run ng serve you will notice that the application is the same as when we had <app-core></app-core> inside of app.component.html. The difference now is that <router-outlet></router-outlet> is dynamically selecting the CoreComponent based on the state of the Angular router.  The <router-outlet></router-outlet> will check for a path corresponding to the routes provided to the RouterModule , in this case routes, and display the component that goes with that path.

In this case we have created two routes, one with the empty path and one with what is known as a wildcard path. Having CoreComponent with the empty path means that when you navigate to localhost:4200 then CoreComponent will be routed to. The wildcard path will match with any unrecognized paths. That means that any path we put after localhost:4200 will display the CoreComponent. I would recommend adding some sort of path not found component and use that with the wildcard path but for now this will do.

RouterModule.forRoot(routes) is providing our defined routes to our application. All additional routes will have to be forChild as I will demonstrate below.

 

Adding Child Routes

Next we are going to add our content to our CoreComponent. This will demonstrate how child routes can be added and work with the <router-outlet></router-outlet> . We want to add the banner and sidebar and then have a content area with another <router-outlet></router-outlet> that our child route will navigate to. We will have it default to the DashboardComponent. Add the following code:

core.component.html

<div class="container-fluid">
  <div class="row">
    <div class="col-12">
      <app-banner></app-banner>
    </div>
    <div class="col-2 sidebar">
      <app-sidebar></app-sidebar>
    </div>
    <div class="col">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

banner.component.html

<nav class="navbar navbar-dark navbar-expand">
  <a class="navbar-brand">Angular.io Demo</a>
</nav>

sidebar.component.html

<div class="d-flex flex-column">
  <div class="p-2">
    Home
  </div>
  <div class="p-2">
    Routing Example
  </div>
</div>

I have added some basic bootstrap and styling classes to layout my components. As you can see, in our CoreComponent we added another <router-outlet></router-outlet> tag. We will have to add child route definitions for the CoreComponent route we previously defined. To do so we will create a new CoreRoutingModule in the core/ folder. As I stated earlier, using the --routing flag when generating the module will also generate the RoutingModule.

  1. Create a coure-routing.module.ts file in your core/ folder.
  2. Add the following code
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { CoreComponent } from './core.component';
const coreRoutes: Routes = [
    {
        path: '',
        component: CoreComponent,
        children: [
            {
                path: '',
                component: DashboardComponent
            },
            {
                path: 'dashboard',
                component: DashboardComponent
            }
        ]
    }
];
@NgModule({
    imports: [RouterModule.forChild(coreRoutes)],
    exports: [RouterModule]
})
export class CoreRoutingModule { }

3. Add CoreRoutingModule to the imports array of the CoreModule
4. Remove the empty path to the CoreComponent in the AppRoutingModule

What we are doing now, is declaring the empty path route to CoreComponent in this module, and then giving child routes to that path. So now when we navigate to localhost:4200 we will get the CoreComponent and then in the <router-outlet></router-outlet> in core.component.html it will dynamically select our DashboardComponent. The second path for ‘dashboard’ means that we will get the same thing if we navigate to localhost:4200/dashboard .

I should point out that the child routes are declared for the empty path route to CoreComponent. Since we have the wildcard route used to get to CoreComponent as well, it should be noted that the child routes will not work with the wildcard route. Basically localhost:4200/somewildcard/dashboard is not going to display the same thing as localhost:4200/dashboard even though the wildcard navigates to the CoreComponent.

 

Depth First Search

A quick note on the functionality of the Angular router. When the Angular router looks through the applications defined routes for a corresponding route definition, it performs a depth first search. For a quick demonstration of this, try the following.

Update your coreRoutes array to look like this

const coreRoutes: Routes = [
    {
        path: '',
        component: CoreComponent,
        children: [
            {
                path: '',
                component: DashboardComponent
            },
            {
                path: 'dashboard',
                component: DashboardComponent
            }
        ]
    },
    {
        path: 'dashboard',
        component: DashboardComponent
    }
];

If you navigate to localhost:4200/dashboard , the RouterOutlet in AppComponent will match the empty string first then search it’s children for ‘dashboard’ to go with the second RouterOutlet. It will find it and display the CoreComponentwith the DashboardComponent in the router-outlet for CoreComponent. The second route to ‘dashboard’ here can never be found.

But if you change the coreRoutes array to look like this

const coreRoutes: Routes = [
    {
        path: 'dashboard',
        component: DashboardComponent
    },
    {
        path: '',
        component: CoreComponent,
        children: [
            {
                path: '',
                component: DashboardComponent
            },
            {
                path: 'dashboard',
                component: DashboardComponent
            }
        ]
    }
];

The Angular router in our AppComponent will see the ‘dashboard’ path before the empty path and just display the DashboardComponent without the CoreComponent.

 

pathMatch

To avoid confusion with the empty string as demonstrated above. I encourage you to use the pathMatch option for your routes.

            {
                path: '',
                pathMatch: 'full',
                redirectTo: 'dashboard'
            },
            {
                path: 'dashboard',
                component: DashboardComponent
            }

Now when we navigate to localhost:4200 the router-outlet in AppComponent will dynamically select CoreComponent and the router-outlet in CoreComponent will see the empty path, and redirect to localhost:4200/dashboard

 

routerLink Example

Assuming you will want to click around your application to navigate, you are going to need to understand the routerLink directive. Here’s a quick example to get you started.

Since I want to treat my examples like features, I am going to create a RoutingExampleModule and a RoutingExampleComponent.

  • ng g module core/routing-example --routing
    • Remember the routing flag will provide a routing module for future use.
  • ng g component core/routing
  • add the RoutingExampleModule to the imports array in CoreModule
  • Update your coreRoutes array in CoreRoutingModule
    • You will probably have to remove the DFS example stuff.
const coreRoutes: Routes = [
    {
        path: '',
        component: CoreComponent,
        children: [
            {
                path: '',
                pathMatch: 'full',
                redirectTo: 'dashboard'
            },
            {
                path: 'dashboard',
                component: DashboardComponent
            },
            {
                path: 'routing-example',
                component: RoutingExampleComponent
            }
        ]
    }
];
  • Update sidebar.component.html to use a routerLink directive to route to our components.
<div class="d-flex flex-column">
  <div class="p-2">
    <a routerLink="/dashboard" routerLinkActive="active">Home</a>
  </div>
  <div class="p-2">
    <a routerLink="/routing-example" routerLinkActive="active">Routing Example</a>
  </div>
</div>

Now when you run the application, clicking the links in the sidebar will change the content displayed by the <router-outlet></router-outlet>in core.component.html.

** routerLinkActive="active" will set the class “active” on the element when the Angular router matches that link. Any class can be set using this method: routerLinkActive="someClassForActive"

 

Summary

With that you should have all the tools you need to get started with application routing in Angular. In a future blog I will demonstrate additional functionality for the Angular router in the RoutingExample section, but for now you should be able to get started with basic routing.