In a fast-paced world, delivering an engaging and responsive user experience is vital. Angular is a widely-used front-end framework developed and maintained by Google. Over the years, Angular gained popularity since its inception due to a vast set of features and robust community support. Routing is a crucial aspect of modern web applications that helps in dynamic user interfaces.
According to the State of JS 2022 survey, Google’s Angular framework had 48.8% usage as the second most popular web framework. Angular offers powerful routing capabilities that make it easy to create complex navigation paths and handle dynamic content.
Without further ado, let’s dig deeper into the Angular router!
Basics of Angular Routing
Routing refers to navigating between different pages or views within a single-page application, a critical feature of Angular development services. In a SPA, the content within the application is loaded dynamically without the need to refresh the page. Angular, as part of its development services, uses the built-in router to handle routing within its applications. The Angular router maps URLs to components. Whenever a user clicks on a link, the router returns the default route mapped to the URL and renders the appropriate app component.
The route configuration is usually defined in the app-routing.module.ts file. Each route has a path that maps to a component. For example, a route for a dashboard page would have path /dashboard and be mapped to DashboardComponent.
The Angular router matches the URL path to the defined routes and loads the associated component into the router outlet. The router outlet acts as a placeholder where the content of the currently selected component is displayed.
Angular routing consists of several key components that work together to enable navigation within the application. These components include:
Component | Description |
RouterModule | Provides necessary directives and services for routing. |
Routes | These are individual routes that are defined in the application. Each route has a path that is linked to a component. |
RouterOutlet | This is a directive that is used to define where the routed component should be displayed in the application. It is placed in the app component. |
RouterLink | It is used to define links that trigger routing within an application. |
Router | Provides methods for navigating between routes of an application. |
ActivatedRoute | This is a service that provides information about the currently activated route. |
Routing Module
Developers can modularize the routing configuration in an application by using Routing Module. To create a router module you can use the Angular CLI to generate a new module with the routing flag.
The following command is used to generate the new module.
ng generate module app-routing --routing
After creating the Routing Module you need to be imported it into the app module. Next you will have to define the routes for the application by creating a routes array. Every Route object in routes array defines a path and a component to render.
Once the routes are defined you will now have to configure the routes by calling the forRoot() method of the RouterModule. It returns a configured RouterModule, which is to be imported into the app module.
Setting up Angular Routing
Let’s look at implementing an Angular router into a real-world application. Imagine you are building a website for a restaurant that has multiple pages. It has a homepage, a menu page, and a contact page. You want to utilize an Angular router to display appropriate angular components. By doing so the users can navigate between the pages easily.
Prerequisites
To implement routing in Angular successfully, you must have the following:
- Node.js: The latest version of Node.js on your machine
- A Code Editor: Any IDE that supports Angular
- npm: Node Package Manager to install the required dependencies
- Angular CLI: The latest version which provides Angular core
Create a New Project
Create a new Angular project using the Angular CLI by running the command in your terminal.
ng new restaurant-website
You can now see a package.json file in your project directory, similar to the one below:
Please note that the versions of dependencies are specified in the above file. These were the versions at the time of the creation of this guide.
Implementing Routing Module
In the src app app.module.ts file and import the RouterModule and Routes modules to implement app routing.
import { RouterModule, Routes } from '@angular/router';
Now add the RouterModule to the imports array in the @NgModule decorator.
@NgModule({ imports: [ RouterModule.forRoot(routes) ], ... })
Create a new file called app-routing.module.ts in src app folder and add the following code for route configuration.
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router';import { HomeComponent } from './home/home.component'; import { MenuComponent } from './menu/menu.component'; import { ContactComponent } from './contact/contact.component'; export const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'menu', component: MenuComponent }, { path: 'contact', component: ContactComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Create three new components for the home page, menu page, and contact page. Run the following commands:
ng generate component home
ng generate component menu
ng generate component contact
In the app.component.html file, add the RouterOutlet component to display the current route’s content.
<router-outlet></router-outlet>
Open the home.component.html file, add the RouterLink directive to create a link to the menu page. This would be the first route when the app loads.
<h2>Abc Restaurant</h2> <p>Welcome to our restaurant! We specialize in delicious food and great service.<br /> Come visit us for a meal you won't forget.</p><br /> <a routerLink="/menu">View Our Menu</a>
In the menu.component.html file, add the RouterLink directive to create a link to the contact page.
<h2>Abc Restaurant - Menu</h2> <p>Here is our menu!</p> <ul> <li>Appetizers: <ul> <li>Chicken Wings</li> <li>Mozzarella Sticks</li> </ul> </li> <li>Entrees: <ul> <li>Grilled Salmon</li> <li>Steak and Fries</li> </ul> </li> <li>Desserts: <ul> <li>Chocolate Cake</li> <li>Apple Pie</li> </ul> </li> </ul><br /><a routerLink="/contact">Contact Us</a>
In the contact.component.html file, add the RouterLink directive to create a link to back to the home page.
<h2>Abc Restaurant - Contact</h2> <p>Address: 123 Main Street<br /> Phone: 555-1234<br /> Email: [email protected]<br /> Hours of operation: Monday-Saturday, 11am-10pm</p><br /> <a routerLink="/">Back to Home</a>
Start the app using the following command ng serve –open and navigate between the home, menu, and contact pages using the links created with RouterLink.
This is how the app would look like in the browser:
Home Page
Menu Page
Contact Page
If you look at the browser address bar, you can see that every page has a different URL path segment.
Advanced Angular Routing Concepts
Let’s look at a few advanced routing concepts in Angular.
1. Nested Routes (Child Routes)
Nested routing allows developers to define a route within a route. The main purpose of this is to create a hierarchical navigation structure that properly shows the hierarchy of multiple components within the application. This makes navigation easier and makes the router configuration more modular.
To configure child routes when creating routes developers need first to define a parent route. This parent route then contains the child routes. They are nested within the parent route using the children property. Let’s look at the example below to see how it’s done.
const routes: Routes = [ { path: 'products', component: ProductsComponent, children: [ { path: ':id', component: ProductDetailsComponent }, { path: '', component: ProductListComponent } ] } ];
2. Route Guards
Route Guards enable developers to control access to routes based on certain conditions. By putting up conditions additional security and unauthorized access is put up on certain routes.
There are three main types of route guards:
- canActivate: determines if a route can be activated.
- canDeactivate: determines if a route can be deactivated.
- canLoad: determines if a route can be lazy loaded.
Let’s look at the example below to see how to implement it.
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; import { Observable } from 'rxjs';@Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor(private router: Router) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { const isLoggedIn = true; // Check if user is logged in if (isLoggedIn) { return true; } else { return this.router.parseUrl('/login'); } } }
3. Lazy Loading
Lazy loading allows the developers to load modules on demand instead of loading all of them at once. This helps in decreasing the initial load time of the application and therefore loads only the necessary modules at a time.
In order to implement lazy load modules in angular you will need to create a feature module for every feature of the application. Then the routes will be configured with the loadChilden property. Let’s look at an example.
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'customers', loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) }, { path: 'orders', loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule) }, { path: '**', component: PageNotFoundComponent } ];
4. Route Parameters
Route parameters allow the developers to pass data between the components and routes. This enables dynamic navigation by providing context to the component using the parameter.
Route parameters are defined using : syntax in the route path and can be required or optional depending on the need. For example, /products/:id defines a route with a required id parameter.
Developers can use ActivatedRoute to access a route parameter. This provides access to current route and its parameters. Route parameters are stored in the params property of the ActivatedRoute object.
Let’s look at an example.
const routes: Routes = [ { path: 'products/:id', component: ProductDetailsComponent } ];// ProductDetailsComponent import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-product-details', templateUrl: './product-details.component.html', styleUrls: ['./product-details.component.css'] }) export class ProductDetailsComponent implements OnInit { productId: string; constructor(private route: ActivatedRoute) { } ngOnInit() { this.route.params.subscribe(params => { this.productId = params['id']; }); } }
In the above example, the ProductDetailsComponent accesses the id parameter using the ActivatedRoute service and subscribes to changes using the params observable.
Best Practices for Angular Routing
Here are a few best practices that you should follow:
1. Organizing Routes and Modules
You should separate routing module for every feature module to keep the code organized and easier to maintain. To improve the flow of the application you can group routes which are related to each other. You should also implement lazy loading to load modules on demand. This increased the initial load time of the application. Moreover, it’s important to use a naming convention for your routes that makes them easy to understand and follow. This helps to avoid confusion for both you and other developers who may be working on the project.
2. Handling Route Errors and 404 Pages
You should use the ** wildcard route to catch all routes that do not match. In order to make the user experience more seamless you can also create a custom component to display a 404 page when a route is not found and you can use the Router.navigate() method to redirect the user to the custom 404 page.
3. Improving Performance With Route Preloading
In route preloading you can use the PreloadAllModules strategy. This strategy preloads all modules in the background. Another strategy that you can use is SelectivePreloadingStrategy. It only preloads specific modules that are likely to be used therefore reducing the initial load time. Moreover, in order to prevent preloading of modules that the user does not have access to you can use the canLoad guard.
By following these above best practices you can create an organized and optimized Angular application.
Testing Angular Router
Unit testing Angular routes means that you have to test the the routing configuration and verify that the route is navigating properly. Integration Testing includes the testing of the behavior of the components during navigation.
RouterTestingModule is used to implement unit testing. Let’s look at an example implementation.
import { RouterTestingModule } from '@angular/router/testing'; import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { AppComponent } from './app.component';describe('AppComponent', () => { let router: Router; let fixture; beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule.withRoutes([])], declarations: [AppComponent] }); fixture = TestBed.createComponent(AppComponent); router = TestBed.inject(Router); }); it('should navigate to the home route', () => { spyOn(router, 'navigateByUrl'); fixture.detectChanges(); expect(router.navigateByUrl).toHaveBeenCalledWith('/'); }); });
To integration test your routes, you can use the Angular RouterTestingModule and the RouterOutlet component to verify that the expected components are loaded when navigating to a specific route. Let’s look at the implementation.
import { RouterTestingModule } from '@angular/router/testing'; import { TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { MenuComponent } from './menu/menu.component'; import { ContactComponent } from './contact/contact.component';describe('AppComponent', () => { let router: Router; let fixture; beforeEach(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([ { path: '', component: HomeComponent }, { path: 'menu', component: MenuComponent }, { path: 'contact', component: ContactComponent } ]) ], declarations: [ AppComponent, HomeComponent, MenuComponent, ContactComponent ] }); fixture = TestBed.createComponent(AppComponent); router = TestBed.inject(Router); }); it('should load the home component when navigating to the home route', () => { fixture.detectChanges(); router.navigate(['']); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('app-home')).toBeTruthy(); }); it('should load the menu component when navigating to the menu route', () => { fixture.detectChanges(); router.navigate(['/menu']); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('app-menu')).toBeTruthy(); }); it('should load the contact component when navigating to the contact route', () => { fixture.detectChanges(); router.navigate(['/contact']); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('app-contact')).toBeTruthy(); }); });
Conclusion
Mastering Angular routing is an essential skill for any modern Angular web developer, including those involved in outsourced Angular software development. With client-side routing being the norm, it’s critical to understand how to create dynamic and efficient routing in your applications. Angular routing provides a flexible and powerful toolset for all sorts of applications and use cases, making it particularly beneficial for outsourced Angular software development projects.
There are plenty of resources available for further learning, including official Angular documentation and online tutorials. Its important to hire a company which offers high quality Angular development services for your Angular project. Before doing so you can it is also important to know whether React or Vue is the best fit for your project instead of Angular since every project has different needs.
If you enjoyed this article, check out:
- Angular Project Structure: Best Practices for Files & Folders
- Dependency Injection in Angular: A Comprehensive Guide
- Mastering Angular Data Binding: A Comprehensive Guide for Expert
- Top Angular UI Component Libraries & Frameworks
- What Is Angular and Why Should Your Business Consider It for Development?
- The Best Javascript Frameworks Today
- Angular for Business
- What Is the Best Framework for Web Development?