Angular Router Tutorial

Photo by Ronise daluz on Unsplash
Photo by Ronise daluz on Unsplash
This article will show you how to set up Angular Router, how to navigate to other routes, and how to pass parameters to other routes.

Angular Router outputs the corresponding components to users according to URLs. Therefore, the routing table also reflects the hierarchical structure of the entire website. This article will show you how to set up Angular Router, how to navigate to other routes, and how to pass parameters to other routes.

The complete code can be found in .

Creating an Angular Router Project

Use the following command to create an Angular project. The –routing option adds Angular Router package into project.

% ng new angular-router-example --routing

After creating a project, use the following command to execute the project.

angular-router-example % ng serve

Open http://localhost:4200/ on browser to see the default screen of Angular project. For later explanation, let’s simplify this complicated screen. Open app.component.html and simplify its content as follows.

<h1>App</h1>
<router-outlet></router-outlet>

After the modification, the homepage will have only a word App.

http://localhost:4200/
http://localhost:4200/

Routes

Adding Routes

It is very simple to set up routes in Angular. Let’s add the first route!

Add a new component called food.

angular-router-example % ng generate component food
CREATE src/app/food/food.component.scss (0 bytes)
CREATE src/app/food/food.component.html (19 bytes)
CREATE src/app/food/food.component.spec.ts (612 bytes)
CREATE src/app/food/food.component.ts (268 bytes)
UPDATE src/app/app.module.ts (467 bytes)

Open app-routing.module.js and add FoodComponent to the routes.

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {FoodComponent} from './food/food.component';

const routes: Routes = [
  {
    path: 'food',
    component: FoodComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {
}

The most important thing for routes is to specify path and component.

  • path: Refers to the path of a route. This path is a relative path, not an absolute path. So path='food' refers to food under the Root path (/), and its absolute path is /food. With the project URL http://localhost:4200/ , its URL will be http://localhost:4200/food .
  • component: Refers to the component displayed according to this path.

After setting food route, browse to http://localhost:4200/food.

http://localhost:4200/food
http://localhost:4200/food

The screen shows that the routes has been successfully set. But the question is, why is the content of AppComponent displayed? This is because Angular Router displays the screen in a hierarchical manner. When /food screen is displayed, Angular Router will first display the component of root path, which is AppComponent. Then, AppComponent uses <router-outlet/> to determine where to display the next level route, which is food.

Configuring Wildcard Routes

When an URL that users tries to browse do not match any route, Angular router will choose Wildcard route.

Open app-routing.module.ts and add a Wildcard route. Wildcard route will display FoodComponent. Of course you can create a PageNotFoundComponent to display an error message showing page does not exist.

const routes: Routes = [
  {
    path: 'food',
    component: FoodComponent,
  },
  {
    path: '**',
    component: FoodComponent,
  },
];

Adding Redirection Routes

Angular Router supports redirection. When users browse a certain URL, Angular Router will automatically redirect users to another URL.

Open app-routing.module.ts and add a redirection route. When users browse root path, they will be redirected to /food.

const routes: Routes = [
  {
    path: 'food',
    component: FoodComponent,
  },
  {
    path: '',
    redirectTo: 'food',
    pathMatch: 'full',
  },
  {
    path: '**',
    component: FoodComponent,
  },
];

After adding it, browse http://localhost:4200/ and you will be redirected to http://localhost:4200/food.

Route Order

The matching order of routes is from top to bottom. When the router matches a route, it starts from the first route in the array and selects the first matched route. Therefore, in general, the Wildcard route will be placed last.

Setting Routing Parameters

We can add variables to a route path, and get the values in the component.

Add a component named beverage.

angular-router-example % ng generate component beverage
CREATE src/app/beverage/beverage.component.scss (0 bytes)
CREATE src/app/beverage/beverage.component.html (23 bytes)
CREATE src/app/beverage/beverage.component.spec.ts (640 bytes)
CREATE src/app/beverage/beverage.component.ts (284 bytes)
UPDATE src/app/app.module.ts (662 bytes)

In app-routing.module.ts, set the route path to /beverage/:name, where :name is a route parameter.

const routes: Routes = [
  {
    path: 'food',
    component: FoodComponent,
    ],
  },
  {
    path: 'beverage/:name',
    component: BeverageComponent,
  },
  {
    path: '',
    redirectTo: 'food',
    pathMatch: 'full',
  },
  {
    path: '**',
    component: FoodComponent,
  },
];

We can use ActivatedRoute.paramMap to get the values of route parameters. Modify the beverage.component.ts as follows:

import {Component, OnInit} from '@angular/core';
import {Observable} from 'rxjs';
import {ActivatedRoute} from '@angular/router';
import {map} from 'rxjs/operators';

@Component({
  selector: 'app-beverage',
  templateUrl: './beverage.component.html',
  styleUrls: ['./beverage.component.scss'],
})
export class BeverageComponent implements OnInit {
  name: Observable<string>;

  constructor(private route: ActivatedRoute) {}

  ngOnInit(): void {
    this.name = this.route.paramMap.pipe(map(params => params.get('name')));
  }
}

name is type of Observable<string> instead of string. In this way, when the route parameter’s value is changed, name will be notified. After obtaining the value of name, we display it in beverage.component.html. It must be displayed with async because it is type of Observable.

<p>beverage {{name | async}} works!</p>

After clicking the link, the following will be displayed.

http://localhost:4200/beverage/cocoloca
http://localhost:4200/beverage/cocoloca

ActivatedRoute

Once Angular router navigates to a route, it creates an ActivatedRoute object for you. This object contains the states of current route. For instance, you can obtain current URL, route parameters, query parameters, route configuration, and so on.

Adding Nesting/Child Routes

When website becomes complex, you need to create nested routes for easy management. Nested routes is also called child routes.

Under food, add a food-detail component.

angular-router-example % ng generate component food/food-detail
CREATE src/app/food/food-detail/food-detail.component.scss (0 bytes)
CREATE src/app/food/food-detail/food-detail.component.html (26 bytes)
CREATE src/app/food/food-detail/food-detail.component.spec.ts (655 bytes)
CREATE src/app/food/food-detail/food-detail.component.ts (295 bytes)
UPDATE src/app/app.module.ts (659 bytes)

Open app-routing.module.ts and add food-detail under food.

const routes: Routes = [
  {
    path: 'food',
    component: FoodComponent,
    children: [
      {
        path: 'detail',
        component: FoodDetailComponent,
      },
    ],
  },
  {
    path: 'beverage/:name',
    component: BeverageComponent,
  },
  {
    path: '',
    redirectTo: 'food',
    pathMatch: 'full',
  },
  {
    path: '**',
    component: FoodComponent,
  },
];

Now, the route path of food-detail is set to /food/detail. However, if you browse http://localhost:4200/food/detail, you cannot see the content of food-detail. Because you didn’t display the child route in FoodComponent. Open food.component.html and use <router-outlet/> to display it child route.

<p>food works!</p>
<router-outlet></router-outlet>

So, child routes are displayed by their parent routes.

Creating Child Route Modules

We add child routes under children of routes. In addition, we can also separate a route and its all child routes to a child route module, which will be easier to manage.

Under the food folder, add a child route module.

angular-router-example % ng generate module food --routing
CREATE src/app/food/food-routing.module.ts (247 bytes)
CREATE src/app/food/food.module.ts (272 bytes)

Remove food route from app-routing.module.

const routes: Routes = [
  {
    path: 'beverage/:name',
    component: BeverageComponent,
  },
  {
    path: '',
    redirectTo: 'food',
    pathMatch: 'full',
  },
  {
    path: '**',
    component: FoodComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {
}

Add food route to food-routing.module.ts.

const routes: Routes = [
  {
    path: 'food',
    component: FoodComponent,
    children: [
      {
        path: 'detail',
        component: FoodDetailComponent,
      },
    ],
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class FoodRoutingModule {
}

Finally, remember to include FoodModule in app.module.ts. Note that it must be placed before AppRoutingModule. Because we have set Wildcard route in AppRoutingModule. If you put FoodModule behind AppRoutingModule, then when browsing http://localhost:4200/food, Angular Router will match Wildcard route first.

@NgModule({
  declarations: [
    AppComponent,
    FoodComponent,
    FoodDetailComponent,
    BeverageComponent,
  ],
  imports: [
    BrowserModule,
    FoodModule,
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {
}

Navigation

Angular Router provides two navigation methods. One is used in .html, which is routerLink. The other is used in the code, which is Router object.

We can use routerLink to provide navigation links in .html.

<h1>App</h1>
<nav>
  <ul>
    <li><a routerLink="/food">Food with absolute path</a></li>
    <li><a routerLink="food">Food with relative path</a></li>
    <li><a routerLink="/beverage/Pepsi">Beverage with path</a></li>
    <li><a [routerLink]="['/beverage', 'CocaCola']">Beverage with route parameter</a></li>
  </ul>
</nav>
<router-outlet></router-outlet>

Modify app.component.html to the following code.

<h1>App</h1>
<nav>
  <ul>
    <li><a routerLink="/food">Food with absolute path</a></li>
    <li><a routerLink="food">Food with relative path</a></li>
    <li><a routerLink="/beverage/Pepsi">Beverage with path</a></li>
    <li><a [routerLink]="['/beverage', 'CocaCola']">Beverage with route parameter</a></li>
  </ul>
</nav>
<router-outlet></router-outlet>
  1. routerLink=”/food”: Navigating with absolute path.
  2. routerLink=”food”: Navigating with relative path.
  3. routerLink=”/beverage/Pepsi”: Specifying :name in path.
  4. [routerLink]=”[‘/beverage’,’CocaCola’]”: When routerLink is an array, the first parameter is a path, and the second parameter is route parameter.

Router

Router can also navigate to specified routes.

Add a link in app.component.ts to trigger show7Up().

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  title = 'angular-router-example';

  constructor(private router: Router) {
  }

  show7Up(): void {
    this.router.navigate(['/beverage', '7UP']);
  }
}

Include Router in the constructor, and call router.navigate() with a path. Its parameter is an array like routerLink, the first is a path, and the second is a route parameter. The route path here is an absolute path.

If you want to use a relative path, you have to include ActivatedRoute as well, and pass ActivatedRoute to router.navigate() to tell it that the route path in the first parameter is relative to the ActivatedRoute.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  title = 'angular-router-example';

  constructor(private router: Router,
              private route: ActivatedRoute) {
  }

  show7Up(): void {
    this.router.navigate(['beverage', '7UP'], {relativeTo: this.route});
  }
}

Finally, modify app.component.html as follows.

<h1>App</h1>
<p>routerLink</p>
<nav>
  <ul>
    <li><a routerLink="/food">Food with absolute path</a></li>
    <li><a routerLink="food">Food with relative path</a></li>
    <li><a routerLink="/beverage/Pepsi">Beverage with path</a></li>
    <li><a [routerLink]="['/beverage', 'CocaCola']">Beverage with route parameter</a></li>
    <li><a (click)="show7Up()">Beverage with router</a></li>
  </ul>
</nav>
<router-outlet></router-outlet>

Passing Parameters

There are two ways to pass parameters to other components. One is route parameters, which we have already discussed. The other is query parameters.

Route Parameters

As mentioned earlier, when route parameters are used, they are values of variables in the path. The code below passes price to /food/detail, but price is not a variable in the path. Add a routerLink with price parameter to app.component.html.

<li><a [routerLink]="['/food/detail', {price: 10}]">FoodDetail with price by route parameter</a></li>

When users click on the link above, it will navigate to http://localhost:4200/food/detail;price=10. URLs with parameters after a semicolon are called Matrix URIs.

We can also pass route parameters programmatically.

this.router.navigate(['/food/detail', {price: 10}]);

We can use ActivatedRoute.paramMap to receive route parameters. Receive routing parameters in food.component.ts.

@Component({
  selector: 'app-food',
  templateUrl: './food.component.html',
  styleUrls: ['./food.component.scss'],
})
export class FoodComponent implements OnInit {
  price: Observable<string>;

  constructor(private route: ActivatedRoute) {
  }

  ngOnInit(): void {
    this.price = this.route.paramMap.pipe(map(params => params.get('price')));
  }
}

We also try receive route parameters in food-detail.component.ts.

@Component({
  selector: 'app-food-detail',
  templateUrl: './food-detail.component.html',
  styleUrls: ['./food-detail.component.scss'],
})
export class FoodDetailComponent implements OnInit {
  price: Observable<string>;

  constructor(private route: ActivatedRoute) {
  }

  ngOnInit(): void {
    this.price = this.route.paramMap.pipe(map(params => params.get('price')));
  }
}

In food.component.html, we display price.

<p>food: price is {{price | async}}</p>
<router-outlet></router-outlet>

Also display price in food-detail.component.html.

<p>food-detail: price is {{price | async}}</p>

Click on the link called FoodDetail with price by route parameter.

http://localhost:4200/food/detail;price=10
http://localhost:4200/food/detail;price=10

As you can see, only FoodDetail receives route parameters, but Food does not. This is because the path is /food/detail.

Only the component of the route path can receive the route parameters passed in.

Query Parameters

In addition, we can also use query parameters to pass parameters. In app.component.html, add a link with size query parameter. Unlike route parameters, values of query parameter should be placed in queryParams.

<li><a routerLink="/food/detail" [queryParams]="{size: 20}">FoodDetail with size by query parameter</a></li>

Clicking on the link above will navigate to http://localhost:4200/food/detail?size=20. This kind of URL with parameters placed after a question mark is called query parameters. Compared with route parameters, we see this kind of URL more often. We can also pass query parameters programmatically.

this.router.navigate( ['/food/detail'], {queryParams: {quantity: 2}});

We can use ActivatedRoute.queryParamMap to receive query parameters. The usage is similar to receiving route parameters. Let’s receive query parameters in food.component.ts.

@Component({
  selector: 'app-food',
  templateUrl: './food.component.html',
  styleUrls: ['./food.component.scss'],
})
export class FoodComponent implements OnInit {
  price: Observable<string>;
  size: Observable<string>;

  constructor(private route: ActivatedRoute) {
  }

  ngOnInit(): void {
    this.price = this.route.paramMap.pipe(map(params => params.get('price')));
    this.size = this.route.queryParamMap.pipe(map(params => params.get('size')));
  }
}

We also receive query parameters in food-detail.component.ts.

@Component({
  selector: 'app-food-detail',
  templateUrl: './food-detail.component.html',
  styleUrls: ['./food-detail.component.scss'],
})
export class FoodDetailComponent implements OnInit {
  price: Observable<string>;
  size: Observable<string>;

  constructor(private route: ActivatedRoute) {
  }

  ngOnInit(): void {
    this.price = this.route.paramMap.pipe(map(params => params.get('price')));
    this.size = this.route.queryParamMap.pipe(map(params => params.get('size')));
  }
}

The following displays size in food.component.html.

<p>food: price is {{price | async}}, size is {{size | async}}</p>
<router-outlet></router-outlet>

Next displays size in food-detail.component.html.

<p>food-detail: price is {{price | async}}, size is {{size | async}}</p>

Click on the link called FoodDetail with size by query parameter.

http://localhost:4200/food/detail?size=20
http://localhost:4200/food/detail?size=20

The path is /food/detail, but both FoodComponent and FoodDetailComponent can receive size parameter.

All components on screen can receive query parameters.

Displaying Multiple Routes with Named Outlets

So far, there is only one <router-outlet/> in a component. Therefore, only one child component can be displayed in its parent component. How to display multiple child components in a component? One way is to use named outlets.

Add a new component called banner.

angular-router-example % ng generate component banner
CREATE src/app/banner/banner.component.scss (0 bytes)
CREATE src/app/banner/banner.component.html (21 bytes)
CREATE src/app/banner/banner.component.spec.ts (626 bytes)
CREATE src/app/banner/banner.component.ts (276 bytes)
UPDATE src/app/app.module.ts (796 bytes)

Add banner to the routes and specify it to be displayed on an outlet called ads.

const routes: Routes = [
  {
    path: 'beverage/:name',
    component: BeverageComponent,
  },
  {
    path: 'banner',
    component: BannerComponent,
    outlet: 'ads',
  },
  {
    path: '',
    redirectTo: 'food',
    pathMatch: 'full',
  },
  {
    path: '**',
    component: FoodComponent,
  },
];

In app.component.html, add a named outlet. Add a routerLink and specify the outlet name and path to be displayed.

...
    <li><a [routerLink]="[{outlets: {ads: ['banner']}}]">Banner</a></li>
  </ul>
</nav>
<router-outlet></router-outlet>
<router-outlet name="ads"></router-outlet>

After clicking on the link called Banner, you can see that the screen shows two outlets, and the path is /food(ads:banner).

http://localhost:4200/food(ads:banner)
http://localhost:4200/food(ads:banner)

in conclusion

Angular Router provides many functions. The functions discussed in this article are basic but also the most commonly used. Since Angular Router is an official package, it is perfectly integrated into Angular. At first, you may feel a bit complicated, but after getting used to it, you will feel quite convenient.

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like