Angular ~ Routing - rohit120582sharma/Documentation GitHub Wiki

Table of contents

  • Introduction
    • A mapping of path & component to change the view of the application by modifying the hash portion of the URL rather than linking to a new page.
  • Setup routes
    • Routes configuration
      • path
      • component/loadChildren
      • children
      • canActivate
      • canActivateChild
      • canDeactivate
      • resolve
      • redirectTo
      • pathMatch
    • Routes registeration into RouterModule.forRoot() method
  • Use routers
    • Loading placeholder
      • <router-outlet> is a directive that marks the spot in the template where the router should display the views.
    • Navigation
      • [routerLink] directive
      • Style router links using [routerLinkActive] directive
      • Navigation programmatically using navigate() method of Router dependency
      • Navigation path - absoulute path (/), relative path (./), or directive way path (../)
  • Route Parameters, Query Parameters, and Fragments
    • Set via [routerLink] from template or navigate() from code level
    • Fetch via ActivatedRoute in code level
  • Route Guards
    • canActivate
    • canActivateChild
    • canDeactivate
    • canLoad
    • resolve

Introduction

Routing is a process of changing the state/view of your Application by loading different components depending upon the URL that the user enters.

You can use RouterModule to route your application to different pages without reloading the entire application which makes application a SPA (Single Page Application).

When a user navigates to a page, Angular router performs the following steps in order:

  • It reads the browser URL the user wants to navigate to
  • It applies a URL redirect (if one is defined)
  • It figures out which router state corresponds to the URL
  • It runs the guards that are defined in the router state
  • It resolves the required data for the router state
  • It activates the Angular components to display the page
  • It manages navigation and repeats the steps above when a new page is requested

Reference


Get started with basic routing in your apps:

Installing the router

The Angular Router is an optional service which is not part of the Angular core. It is in its own library package, @angular/router. So we need to install the router first.

npm install @angular/router --save

Base tag

To get our application ready to make use of routing we must set our base tag. It tells the browser which base URI to use for relative URI paths.

<base href="/">

Module importing and configuration

  • Import RouterModule and Routes in your app module (app.module.ts).

  • RouterModule imported in the main app module makes the router available everywhere in your app. This module provides all exported elements, directives, service to be used in the children components underneath the module;

  • Routes is a typescript type of Route[], an array of individual Route instances. Each Route is an object mapping a URL path to a component. This is basically your routing configuration details.

    • The path property describes the URL this route will handle.
    • The component property is the name of the component we want to display when the URL in the browser matches this path.
  • You can configure the router and install via following method, and adds the result to the Module's imports array.

    • RouterModule.forRoot(routes) - creates a routing module that includes the router directives, the route configuration and the router service
    • RouterModule.forChild(routes) - creates a routing module that includes the router directives, the route configuration but not the router service. The RouterModule.forChild() method is needed when your application has multiple routing modules.
import { RouterModule, Routes } from '@angular/router';

// Routes configuration
const appRoutes: Routes = [
	{
		path: 'projects/:id',
		component: ProjectDetailComponent
	},
	{
		path: 'projects',
		component: ProjectListComponent
	},
	{
		path: 'users',
		component: UserListComponent,
		data: { title: 'Users List' }
	},
	{
		path: '',
		redirectTo: '/users',
		pathMatch: 'full'
	},
	{
		path: '**',
		component: PageNotFoundComponent
	}
];

// Installing routing
@NgModule({
	imports: [
		RouterModule.forRoot(
			appRoutes,
			{ enableTracing: true } // <-- debugging purposes only
		)
		// other imports here
	],
	...
})
export class AppModule {
}
  • Angular router is dynamic routes and have other options and features like:
    • The router uses a first-match wins strategy when matching routes, so more specific routes should be placed above less specific routes.
    • The :id in the second route is a token for a route parameter.
    • The data property in the third route is a place to store arbitrary data associated with this specific route. The data property is accessible within each activated route. Use it to store items such as page titles, breadcrumb text, and other read-only, static data.
    • The empty path in the fourth route represents the default path for the application, the place to go when the path in the URL is empty, as it typically is at the start. This default route redirects to the route for the /users URL and, therefore, will display the UserListComponent. A redirect route requires a pathMatch property to tell the router how to match a URL to the path of a route. The router throws an error if you don't.
    • The ** path in the last route is a wildcard. The router will select this route if the requested URL doesn't match any paths for routes defined earlier in the configuration. This is useful for displaying a "404 - Not Found" page or redirecting to another route.

Template with

<router-outlet> is a directive that marks the spot in the template where the router should display the views for that outlet.

So after configuring our routes, the next step is to tell Angular where to load the components. When the router has matched a route and found the component(s) to load, it will dynamically create our component and inject it as a sibling alongside the <router-outlet> element.

<router-outlet></router-outlet>
<!-- Routed views go here -->

RouterLink

To allow us to link to our routes, we can use a directive called routerLink. Anchor tags use the routerLink binding instead of the href attribute to point to specific routes. This works the same as href in this case.

It can accepts an array which constructs segments of the URL that we want to navigate to.

<a [routerLink]="['/profile', username]">
	Go to {{ username }}'s profile.
</a>

Navigation Paths

Absolute path with a / at the beginning will add from root domain.

Relative path without a slash or with a ./ at the beginning will add from currently loaded path. It also support CMD style for directory navigation like ../ which means go one path back from currently loaded path.

<a [routerLink]="['/profile']">Profile : Absolute path</a>

<a [routerLink]="['profile']">Profile : Relative path</a>

<a [routerLink]="['../profile']">Profile : Relative path with CMD style</a>

RouterLinkActive binding

It’s useful for us to indicate to the user which route is currently active, we typically do this by adding a class to the link that’s active.

To add this functionality to our routerLink, we can use the directive routerLinkActive as property-binding. It takes as input an array of classes which it will add to the element it’s attached to if it’s route is currently active.

<nav>
	<a [routerLink]="['settings']" [routerLinkActive]="['active']">Home</a>
	<a [routerLink]="['settings', 'password']" [routerLinkActive]="['active']">Change password</a>
	<a [routerLink]="['settings', 'profile']" [routerLinkActive]="['active']">Profile Settings</a>
</nav>

Parameterised Routes

Sometimes we need part of the path in one or more of our routes (the URLs) to be a variable. That’s called a parameterised route.

Route Parameters

Linking to Routes with Parameters

  • A path can have a variable begins with a colon :
  • A path can have any number of variables as long as they all start with : and have different names.
  • Non-parameterised routes always take priority over parameterised routes.
const routes: Routes = [
	{ path: 'product/:id', component: ProductDetailComponent },
	{ path: 'product/moo', component: MooComponent },
	{ path: 'product', component: ProductListComponent }
];
  • Alternatively we could navigate to the route programmatically using the Router service:
goToProductDetails(id) {
	this.router.navigate(['product', id]);
}

Reading Route Parameters

  • The ActivatedRoute service provides a params Observable which we can subscribe to to get the route parameters
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
	...
})
export class ProductDetailComponent implements OnInit, OnDestroy {
	private _id:number;
	private _sub:any;

	constructor(private route:ActivatedRoute){
	}
	ngOnInit(){
		this._sub = this.route.params.subscribe((params)=>{
			this._id = +params['id']; // (+) converts string 'id' to a number
		});
	}
	ngOnDestroy() {
		this._sub.unsubscribe();
	}
}

Query Parameters

Passing Optional Parameters

  • Query parameters allow you to pass optional parameters to a route such as pagination information.
  • The key difference between query parameters and route parameters is that route parameters are essential to determining route, whereas query parameters are optional.
  • Use the [queryParams] directive along with [routerLink] to pass query parameters.
<a [routerLink]="['product-list']" [queryParams]="{ page: 99 }">Go to Page 99</a>
  • Alternatively, we can navigate programmatically using the Router service:
goToPage(pageNum) {
	this.router.navigate(['product-list'], { queryParams: { page: pageNum } });
}

Reading Query Parameters

  • Similar to reading route parameters, the ActivatedRoute service returns an Observable we can subscribe to to read the query parameters:
this.route.queryParams.subscribe((params)=>{
	// Defaults to 0 if no query param provided.
	this.page = +params['page'] || 0;
});

Fragements

Passing Fragements

Reading Fragements

Matrix URL

  • Angular also supports optional routes via passing in an object to the navigate function. This is called Matrix URL notation, a series of optional key=value pairs separated with the semicolon ; character.
  • Now when we perform a search the URL looks like /search;term=U2
onSearch(term:string) {
	this.router.navigate(['search', {term: term}]);
}

Route Guards

Any user can navigate anywhere in the application anytime. With guards we can add checks to restrict access to a user to certain pages on our site.

A route can be configured with multiple guards and the guards are checked in the order they were added to the route.

We can add guards to the route configuration to handle following scenarios:

  • Perhaps the user is not authorized to navigate to the target component.
  • Maybe the user must login (authenticate) first.
  • Maybe you should fetch some data before you display the target component.
  • You might want to save pending changes before leaving a component.
  • You might ask the user if it's OK to discard pending changes rather than save them.

Types of Guards

  • CanActivate - checks to see if a user can visit a route.
  • CanActivateChild - checks to see if a user can visit a routes children.
  • CanDeactivate - checks to see if a user can exit a route.
  • CanLoad: - checks to see if a user can route to a module that lazy loaded.

Guards are implemented as services that need to be provided so we typically create them as @Injectable classes. Since they are just classes and as such can have any other dependencies injected into their constructor so can work in conjunction with other services to figure out if the guard passes or fails.

Guard functions can return either a boolean or an Observable or Promise which resolves to a boolean at some point in the future.

Guard function parameters

  • component: Component - this is the component itself.
  • route: ActivatedRouteSnapshot - this is the future route that will be activated if the guard passes, we can use it’s params property to extract the route params.
  • state: RouterStateSnapshot - this is the future RouterState if the guard passes, we can find the URL we are trying to navigate to from the url property.

Steps to create and assign a guard

  • First we need to import the interface for guards
  • Then need to create an Injectable class which implements the interface with function(s) like canActivate etc. The function(s) should return boolean(true/false) value.
  • Then we need to add this service as providers in app.module.ts
  • Finally we need to add this guard to one or more of our routes

app.module.ts

import { RouterGuardService } from './router-gaurd';
import { ProjectsChildRoutes } from './projects.router.ts';

const RootRoutes: Routes = [
	{
		path: 'projects',
		loadChildren: './projects/projects.module',
		canLoad: [RouterGuardService]
	}
];
@NgModule({
	imports: [
		RouterModule.forRoot(RootRoutes),
		RouterModule.forChild(ProjectsChildRoutes)
	],
	providers: [
		RouterGuardService
	]
})
export class AppModule {
}

projects.router.ts

export const ProjectsChildRoutes:Routes = [
	{
		path: ':id',
		component: ProjectDetailComponent,
		canActivate: [RouterGuardService],
		canDeactivate: [RouterGuardService],
		children: [
			{
				{
					path: 'tasks',
					component: TaskListComponent
				},
				{
					path: 'tasks/:id',
					component: TaskDetailComponent
				},
				{
					path: '',
					redirectTo: 'tasks',
					pathMatch: 'full'
				},
			}
		]
	}
];

project-detail.component.ts

import { Component } from '@angular/core';
import { Observable }  from 'rxjs/Observable';
import { CanComponentDeactivate } from './router-gaurd.service.ts';

@Component({
	selector: 'app-project-detail',
	template: '<p>Project Detail Component is instantiated!</p>'
})
export class ProjectDetailComponent implements CanComponentDeactivate {
	constructor() {
	}
	canDeactivate() : Observable<boolean> | Promise<boolean> | boolean {
		return confirm('Do you want to leave this page?');
	}
}

router-gaurd.service.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate, CanActivateChild, CanDeactivate, CanLoad, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable }  from 'rxjs/Observable';

export interface CanComponentDeactivate {
	canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
 
@Injectable()
export class RouterGuardService implements CanActivate, CanActivateChild, CanDeactivate<CanComponentDeactivate>, CanLoad {
	constructor(private router: Router) {
	}

	canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):boolean {
		let url: string = state.url;
		return confirm('Do you want to navigate this page?');
	}
	canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):boolean {
		return this.canActivate(route, state);
	}
	canDeactivate(component: CanComponentDeactivate, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
		// Get the Crisis Center ID
		console.log(route.paramMap.get('id'));

		// Get the current URL
		console.log(state.url);

		// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
		if (!component.crisis || component.crisis.name === component.editName) {
			return true;
		}
		// Otherwise ask the user with the dialog service and return its
		// observable which resolves to true or false when the user decides
		return component.canDeactivate();
	}
	canLoad():boolean {
		return confirm('Do you want to load this lazy module?');
	}
}
⚠️ **GitHub.com Fallback** ⚠️