Angular Route Guard Guard routings
▌Introduction
In Angular, we can use Route Guard to protect our routing while a user may not has the permission to navigate some of our routes.
In other hand, if we don’t want the user to leave current
route, CanDeactivate can help us
to detect that routing is changing and stop the leaving. It’s often used to
confirm the leaving when current Form data is dirty(Changed).
▌Environment
▋Angular 2.4.0
▋Angular CLI
1.0.0-beta.31
▌CanActivate
Here is a sample of routing module.
▋cms-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
//...
const routes: Routes =
[
{ path: 'Login', component:
LoginComponent},
{
path: 'Layout', component: LayoutComponent
children: [
{ path:
'Index', component: IndexComponent, },
{ path:
'Product/List', component: ProductListComponent,},
{ path:
'Product/Create', component: ProductCreateComponent},
{ path:
'Product/Edit/:id', component: ProductEditComponent},
{ path:
'Link', component: LinkComponent},
]
}];
@NgModule({
imports:
[RouterModule.forChild(routes)],
exports:
[RouterModule],
providers: []
})
export class CmsRoutingModule { }
|
So the scenario is that we wanna guard the “Layout” route
and its child routes, which are only available for login users.
First, we have to implement a class: LoginRouteGuard,
which will decides if the demand route could be activated or not.
▋LoginRouteGuard
▋login-route-guard.ts
import { Injectable } from '@angular/core';
import { Router,
ActivatedRoute, CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot} from '@angular/router';
import { Observable } from 'rxjs';
import { LoginService } from './services/login.service';
@Injectable()
export class LoginRouteGuard implements CanActivate {
constructor(
private router: Router,
private loginSvc:
LoginService) {
}
canActivate(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot) {
let currentUri = state.url; //Get current uri
if (this.loginSvc.checkIsAllowed(currentUri)) {
return Observable.of(true);
}
else {
this.router.navigate(['/Cms/Login']);
}
}
}
|
Notice that when a user is not allowed to navigate the
protected route, he/she will be redirected to login page.
▋ cms-routing.module.ts
Let’s update our routing module to support LoginRouteGuard.
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { LoginRouteGuard } from './login-route-guard';
//...
const routes: Routes =
[
{ path: 'Login', component:
LoginComponent },
{
path: 'Layout', component: LayoutComponent,
canActivate: [LoginRouteGuard], children: [
{ path:
'Index', component: IndexComponent, canActivate: [LoginRouteGuard]
},
{ path:
'Product/List', component: ProductListComponent, canActivate: [LoginRouteGuard]},
{ path:
'Product/Create', component: ProductCreateComponent, canActivate:
[LoginRouteGuard]},
{ path:
'Product/Edit/:id', component: ProductEditComponent, canActivate:
[LoginRouteGuard]},
{ path:
'Link', component: LinkComponent},
]
}];
@NgModule({
imports:
[RouterModule.forChild(routes)],
exports:
[RouterModule],
providers: []
})
export class CmsRoutingModule { }
|
▋Demo
Okay, it’s done. Assume that the current user could
navigate
/Cms/Layout/Index
But has no permission on
/Cms/Layout/Product/List
Here is a demo.
▋CanActivateChild
Sure the
Angular Route Guard works cool, but we don’t want to set
canActivate: [LoginRouteGuard]
on every child
route of “Layout”. So we will apply the LoginRouteGuard to all of them with CanActivateChild.
▋login-route-guard.ts
import { Injectable } from '@angular/core';
import { Router,
ActivatedRoute, CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot, CanActivateChild } from '@angular/router';
import { Observable } from 'rxjs';
import { LoginService } from './services/login.service';
@Injectable()
export class LoginRouteGuard implements CanActivate, CanActivateChild {
//Skip the exist codes...
canActivateChild(childRoute:
ActivatedRouteSnapshot, state: RouterStateSnapshot){
return this.canActivate(childRoute, state);
}
}
|
▋ cms-routing.module.ts
const routes: Routes =
[
{ path: 'Login', component:
LoginComponent },
{
path: 'Layout', component:
LayoutComponent, canActivate: [LoginRouteGuard], canActivateChild:
[LoginRouteGuard], children: [
{ path:
'Index', component: IndexComponent, },
{ path: 'Product/List', component:
ProductListComponent,},
{ path:
'Product/Create', component: ProductCreateComponent},
{ path:
'Product/Edit/:id', component: ProductEditComponent},
{ path:
'Link', component: LinkComponent},
]
}];
//...
|
▌CanDeactivate
In the following sample, we
are going to implement the InputRouteGuard to inform
users that the form data is changed and confirmed the leaving without
saving the dirty form data.
▋InputRouteGuard
▋input-route-guard.ts
/// <reference
path="../../../../assets/lib-npm/typings/sweetalert.d.ts" />
import { Injectable } from '@angular/core';
import { Router,
ActivatedRoute, RouterStateSnapshot, ActivatedRouteSnapshot, CanDeactivate } from '@angular/router';
import { FormBaseComponent } from './../components/form-base/form-base.component';
declare var swal: any;
@Injectable()
export class InputRouteGuard implements CanDeactivate<FormBaseComponent> {
constructor(private router: Router) {
}
canDeactivate(component: FormBaseComponent,
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot) : Promise<boolean> {
if (component.form.dirty) {
return new
Promise<boolean>(
resolve => {
swal({
title: 'Are you sure?',
text: 'Unsaved changes will be lost!',
type: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, ignore it.',
cancelButtonText: 'No'
}).then(function (isOk: boolean) {
resolve(isOk);
}, function (dismiss) {
resolve(false);
})
});
} else {
return new Promise( resolve
=> resolve(true));
}
}
}
|
In canDeactivate
function, return true for leaving
current route, or false to stay.
▋form-base.component
import { Component, OnInit,
ViewChild} from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'share-form-base',
template: ''
})
export class FormBaseComponent implements OnInit {
@ViewChild(NgForm) form: NgForm;
constructor()
{ }
ngOnInit() {
}
}
|
▋form-base.component
import { NgModule } from '@angular/core';
import { Routes,
RouterModule, CanDeactivate } from '@angular/router';
import { InputRouteGuard } from './../share/route-guard/input-route-guard';
//...
const routes: Routes =
[
{ path: 'Login', component:
LoginComponent, canDeactivate: [InputRouteGuard] },
{
path: 'Layout', component:
LayoutComponent, canActivate: [LoginRouteGuard], canActivateChild: [LoginRouteGuard],
children: [
{ path:
'Index', component: IndexComponent, },
{ path:
'Product/List', component: ProductListComponent,},
{ path:
'Product/Create', component: ProductCreateComponent, canDeactivate: [InputRouteGuard]},
{ path:
'Product/Edit/:id', component: ProductEditComponent, canDeactivate:
[InputRouteGuard]},
{ path:
'Link', component: LinkComponent},
]
}];
@NgModule({
imports:
[RouterModule.forChild(routes)],
exports:
[RouterModule],
providers: []
})
export class CmsRoutingModule { }
|
▋cms-routing.module.ts
Update the routing
to support InputRouteGuard
on the following routes:
1. /Cms/Login
2. /Cms/Layout/Product/Create
3. /Cms/Layout/Product/Edit
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { InputRouteGuard } from './../share/route-guard/input-route-guard';
//...
const routes: Routes =
[
{ path: 'Login', component:
LoginComponent, canDeactivate:
[InputRouteGuard] },
{
path: 'Layout', component:
LayoutComponent, canActivate: [LoginRouteGuard], canActivateChild: [LoginRouteGuard],
children: [
{ path:
'Index', component: IndexComponent, },
{ path:
'Product/List', component: ProductListComponent,},
{ path:
'Product/Create', component: ProductCreateComponent, canDeactivate:
[InputRouteGuard]},
{ path:
'Product/Edit/:id', component: ProductEditComponent, canDeactivate:
[InputRouteGuard]},
{ path:
'Link', component: LinkComponent},
]
}];
@NgModule({
imports:
[RouterModule.forChild(routes)],
exports:
[RouterModule],
providers: []
})
export class CmsRoutingModule { }
|
▋Demo
▌Reference
沒有留言:
張貼留言