Angular is one of the most popular front-end frameworks, with 30% of developers preferring it for their projects. Angular is especially useful for large scale projects with many different views and components.
The key to making these large scale projects engaging is a logical navigation structure that allows users to easily explore and revisit pages. Thankfully, Angular's routing functionality makes optimizing navigation simple.
Today, we'll learn more about Angular Router's capabilities and help you make a fully navigable app.
Here’s what we’ll cover today:
- What is Angular Router
- What are Wildcard Routes
- Child Routes
- The RouterLink Directive
- Adding ActiveLink Styles
- Lazy loading modules
- What to learn next
Design complex Angular apps with ease
Learn to use all the best Angular tools to create stunning and efficient web apps.
Angular: Designing and Architecting Web Applications
What is Angular Router?
The Angular Router is an importable package built-in to Angular 2+ by default. It's used to build Single Page Applications with multiple views that can be navigated by URL, known as "routing".
URLs consist of a domain name and a route definition, known as a path. A path is a JavaScript object that the server uses to access a specific resource in the database. When the server serves our application, Angular will grab the path from the URL and match it against any valid paths we've set up. Essentially, we set a key/value relationship with a path like /blog
as the key and the desired page as the value
.
This allows users to easily navigate your app and visit the page they want without having to start at the home component. Routing enables support for common browser behaviors like forward/back arrows and page bookmarking.
Router also contains tools for advanced behaviors like multiple router outlets, different path matching strategies, easy access to route parameters, and route guards to protect components from unauthorized access.
Routing Module and RouterOutlet
Routing Modules are special Angular modules that define new routes and help configure the router. All routing modules have the suffix -routing
after their name, which is automatically added by Angular CLI.
Every routing module sets the routing behavior for a paired module with the same base name. For example, the routing behavior for our home
module would be in the routing module home-routing
.
Here's an example of a routing module for our home
module, called home-routing.module.ts
:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeMainComponent } from './home-main/home-main.component';
const routes: Routes = [
{ path: '', component: HomeMainComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HomeRoutingModule { }
You can find our routes in the routes
array variable. Each element of the routes
array represents the route to a single component view.
The elements consist of two parts, the path
property that provides a URL path and the component
property that defines which component will be loaded at the provided path.
In this case, we enter an empty string (interpreted as a forward slash) to indicate that this component is our homepage and should load if someone just enters the domain name. We then enter the name of the component Angular should fetch as our homepage, HomeMainComponent
.
We can also use
redirectto
to direct empty path URLs to another page if we'd prefer users land elsewhere.
Next, we'll need to remove HomeMainComponent
from the exports of HomeModule
. This use of routing means we're no longer exporting the component and instead let Router take care of loading the component if a user visits the route.
Finally, we'll replace the contents of the app.component.html
file with the line:
<router-outlet></router-outlet>
Here, <router-outlet>
acts as a placeholder for the component. Instead of defining a component, our template will simply pull whatever component is rendered with the passed URL path. By using this placeholder, we don’t have to export the component. Instead, we can export the module.
You can now view this app by entering http://localhost:4200
in your browser address bar.
To review, the HomeRoutingModule
is a routing module where we define routes. We have one route that consists of a blank path. We’ll check if the client's URL entry matches that path. If they are, we’ll load the homepage via HomeMainComponent
.
The homepage component is available due to a series of imports. First, we import the home-routing
module into its paired standard module home
. Then, we import the home
module into the app module. Finally, we use the <router-outlet>
directive in the app.component.html
file to load the HomeMainComponent
registered in the original
routes
array.
What are Wildcard Routes?
What happens when a user enters an invalid path? We can avoid an error by including a Wildcard Route, which catches all unregistered paths and directs them to a certain page. You can think of Wildcards as an "other" category that reads as a match to any unregistered paths.
Most sites have a Wildcard that directs to a "404 Page Not Found" page. To create an error component for our app, enter the following into your command prompt:
ng generate component PageNotFound
We do not need a module because this error screen will just be simple text.
You can set a Wildcard by entering **
in place of a standard path in the routes
array.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
const routes: Routes = [
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Now, any invalid URL will redirect to our 404 error page.
We need to make sure this component is imported last in the app.module.ts
file for our other components to load properly. This is because Angular loads the component from the first matching path. If AppRoutingModule
is imported first, Angular would always load PageNotFoundComponent
because the Wildcard would always read as a match and therefore Angular would return that component.
imports: [
BrowserModule,
HomeModule,
AboutModule,
ContactModule,
AppRoutingModule,
],
The Wildcard at the bottom of the imports
array ensures that any valid matches are returned and the 404 is only returned if there are no other matches.
Child Routes
Sometimes it makes sense to have routes categorized as a subgroup within a route. For example, our "About Us" page could feature separate subpages for info on the employees, /about/team
and info on past clients, /about/clients
. Child components are only rendered if the user is on the parent /about
path.
First, we'll generate the components by entering the following into our command prompt:
ng generate component about/team
ng generate component about/clients:
We then set these as children of the "About Us" page by adding a children
array property to the about
route in about-routing.module.ts
.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AboutMainComponent } from './about-main/about-main.component'
import { BioComponent } from './bio/bio.component';
import { TeamComponent } from './team/team.component';
import { ClientsComponent } from './clients/clients.component';
const routes: Routes = [
{
path: '',
component: AboutMainComponent,
children: [
{ path: '', component: BioComponent },
{ path: 'team', component: TeamComponent },
{ path: 'clients', component: ClientsComponent },
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AboutRoutingModule { }
The children
array acts like a smaller version of the routes
array, with similarly-formatted path
and component
properties. The difference is that child routes' path properties are appended onto their parent path, meaning you do not need to write the full path.
For example, the full path to reach TeamComponent
would be /about/team
rather than just '/team'
.
Finally, we'll update the about-main.component.html
template file with <router-outlet>
to let it show any of the child components of about
.
<h1>About Page</h1>
<router-outlet></router-outlet>
Continue learning Angular.
Pick up Angular in half the time. Educative's hands-on courses let you learn top industry skills with real-world practice, not lengthy video lectures. By the end, you'll know how to create fully-fledged Angular apps.
Angular: Designing and Architecting Web Applications
The RouterLink Directive
Most apps will allow users to navigate with a specific URL and user-clicks on link elements. We'll need Bootstrap to add links. You can make links using standard href
attributes. However, that requires the site refresh and assets reload whenever the page changes.
We can speed up loading using Angular Router's routerLink
directive, which leverages history API to let Angular access your immediate browser history. This means browsers only need to load each page once, as any later visits can display the previously loaded elements.
To implement routerLink
, replace the contents of app.component.html
with:
<nav class="navbar navbar-expand-md navbar-light bg-light mb-4">
<a class="navbar-brand" routerLink="/">Website</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" routerLink="/about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/contact">Contact</a>
</li>
</ul>
</div>
</nav>
<main class="container">
<div class="card">
<div class="card-body">
<router-outlet></router-outlet>
</div>
</div>
</main>
The URL in the address bar will still change when navigating through these links, but the browser will not refresh the page or reload assets on return visits.
Adding ActiveLink Styles
Building on our new navigation links, we also need a way to tell the user which page they're currently on. The best way to do this in Angular is to use the active
class, which will change the style of a link if they're currently on to indicate it is active.
In Bootstrap, the active
class can be applied to the <li>
element wrapped around the <a>
element. We'll use Angular's routerLinkActive
directive to achieve this.
<nav class="navbar navbar-expand-md navbar-light bg-light mb-4">
<a class="navbar-brand" routerLink="/">Website</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/about">About</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/contact">Contact</a>
</li>
</ul>
</div>
</nav>
<main class="container">
<div class="card">
<div class="card-body">
<router-outlet></router-outlet>
</div>
</div>
</main>
We’re applying the directive on the <li>
elements with the nav-item
class. This directive will check if the URL in the address bar matches the path in the routerLink
directive.
If the path matches, we'll add it to the active
class to change the link text to show that it's active with the darker text color.
Lazy loading modules
We can improve the performance of our module by transitioning from eager loading to lazy loading.
Eager loading is when the browser is directed to load all components within the app
module, regardless of which it will use.
Lazy loading instead splits the module into separate files so the app only loads the components it needs for the current page render. Lazy loading is often preferred as it allows the page to load the minimum amount of data for each render and therefore speeds up loading.
To implement lazy loading, we first remove all module imports from app.module.ts
:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
@NgModule({
declarations: [
AppComponent,
PageNotFoundComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
We still eager load PageNotFoundComponent
, as it is low weight, and we could need it at any point.
We also need to update this information in the routes
array found in app-routing.module.ts
. This is the only routing module that will be sent at the user's initial request. Angular can then use this file to load any future modules as needed.
const routes: Routes = [
{ path: '', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
{ path: 'about', loadChildren: () => import('./about/about.module').then(m => m.AboutModule) },
{ path: 'contact', loadChildren: () => import('./contact/contact.module').then(m => m.ContactModule) },
{ path: '**', component: PageNotFoundComponent },
];
Notice that we’re not using the component property to tell Angular what component to load when the route is visited. Instead, we’re using the loadChildren
property. This will tell Angular to lazy load a module. We’re setting it to an arrow function, which will request the module through the import()
function. The import()
function returns a promise. We chain the then()
function to handle the response.
These loadChildren
paths will pass along any previous path elements as a prefix for later paths. We must therefore update each of our routing modules' Routes
array to empty paths to ensure we do not repeat path names like /about/about
.
{ path: '', component: AboutMainComponent }
What to learn next
Congratulations on making a fully navigable Angular application! Routing is the key to keep users engaging with your app, especially for large applications. However, it's just one part of making an excellent Angular app.
Here are some more advanced concepts you're ready to tackle along your Angular journey:
- Advanced Routing (private routes, pairing CSS stylesheets)
- Lifecycle Hooks
- Modal Components
- Authentication
- Dependencies
To help you study these topics, Educative has created Angular: Designing and Architecting Web Applications. This course teaches you how to create large scale Angular applications in a logical and efficient way using advanced Angular techniques. You'll even build a full-fledged application alongside the course.
By the end, you'll have hands-on experience, plus a project for your professional portfolio.
Happy learning!
Top comments (0)