Protecting routes is a very common task when building applications, as we want to prevent our users from accessing areas that they’re not allowed to access, or, we might want to ask them for confirmation when leaving a certain area. Angular’s router provides a feature called Navigation Guards that try to solve exactly that problem. In this article, we’d like to take a look at the different types of guards and how to implement them for actual use cases.
Guard Types
There are different guard types we can use to protect our routes:
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.
Resolve: Performs route data retrieval before route activation. https://angular.io/
CanLoad: Checks to see if a user can route to a module that lazy loaded.
For a given route we can implement zero or any number of Guards.
We’ll go through the first three as the last two are very advanced use cases and need lazy loading modules which we we haven’t covered.
This guard returns true all the time, so doesn’t really guard anything. It lets all users through but at the same time our guard logs "AlwaysAuthGuard" to the console so we can at least see when it’s being used.
We need to provide this guard, for this example lets configure it via our NgModule, like so:
We created a new CanActivate guard called OnlyLoggedInUsersGuard
We add OnlyLoggedInUsersGuard to the list of guards for our route.
Now when we try to navigate to the search view we are blocked from doing so and shown a window alert.
If we want to redirect users to a login page we may inject Router into the constructor and then use the navigate function to redirect them to the appropriate login page.
Note: So the rest of the samples in this chapter work we will change the isLoggedIn function on our UserService to return true instead.
CanActivateChild
As well as CanActivate we also have CanActivateChild which we implement in similar way.
Lets do the same as the CanActivate example and create a guard called AlwaysAuthChildrenGuard.
CanDeactivate:
Guard ask permission to discard unsaved changes.
Guard Types
There are different guard types we can use to protect our routes:
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.
Resolve: Performs route data retrieval before route activation. https://angular.io/
CanLoad: Checks to see if a user can route to a module that lazy loaded.
We’ll go through the first three as the last two are very advanced use cases and need lazy loading modules which we we haven’t covered.
CanActivate
Guards are implemented as services that need to be provided
so we typically create them as @Injectable classes.
Guards return either true if the user can access a route or
false if they can’t.
Lets create a simple CanActivate guard.
First we need to import the CanActivate interface, like so:
import {CanActivate} from "@angular/router";
Then lets create an Injectable class called AlwaysAuthGuard
which implements the canActivate function, like so:
class AlwaysAuthGuard implements CanActivate {
canActivate() {
console.log("AlwaysAuthGuard");
return true;
}
}
We need to provide this guard, for this example lets configure it via our NgModule, like so:
@NgModule({
.
.
providers: [
.
.
AlwaysAuthGuard
]
})
Finally we need to add this guard to one or more of our
routes, lets add it to our ArtistComponent route like so:
const routes: Routes = [
{path: '', redirectTo: 'home', pathMatch: 'full'},
{path: 'find', redirectTo: 'search'},
{path: 'home', component: HomeComponent},
{path: 'search', component: SearchComponent},
{
path: 'artist/:artistId',
component: ArtistComponent,
canActivate: [AlwaysAuthGuard],
children: [
{path: '', redirectTo: 'tracks'},
{path: 'tracks', component: ArtistTrackListComponent},
{path: 'albums', component: ArtistAlbumListComponent},
]
},
{path: '**', component: HomeComponent}
];
We added our AlwaysAuthGuard
to the list of canActivate guards
for this route.
Note: Since it holds an array we could have multiple guards for a single route.
Note: If this was a canActivateChild guard we would be adding it to the canActivateChild property and so on for the other guard types.
Now every-time we navigate to the ArtistComponent route we get "AlwaysAuthGuard" printed to the console so we know that the AlwaysAuthGuard is working.
OnlyLoggedInUsersGuard
The most typical use case for the CanActivate guard is some form of checking to see if the user has permissions to view a page.
Normally in an Angular application we would have a service which held whether or not the current user is logged in or what permissions they have.
Lets create another guard called OnlyLoggedInUsersGuard which only allows logged in users to view a route.
@Injectable()
class OnlyLoggedInUsersGuard implements CanActivate {
constructor(private userService: UserService) {};
canActivate() {
console.log("OnlyLoggedInUsers");
if (this.userService.isLoggedIn()) {
return true;
} else {
window.alert("You don't have permission to view this page");
return false;
}
}
}
We inject and store UserService into the constructor for our class.
If the user is logged in the guard passes and lets the user through.
If the user is not logged in the guard fails, we show the user an alert and the page doesn’t navigate to the new URL.
Finally we need to add this guard to the list of guards for our search route, like so:
If the user is logged in the guard passes and lets the user through.
If the user is not logged in the guard fails, we show the user an alert and the page doesn’t navigate to the new URL.
Finally we need to add this guard to the list of guards for our search route, like so:
{
path: 'artist/:artistId',
component: ArtistComponent,
canActivate: [OnlyLoggedInUsersGuard, AlwaysAuthGuard],
children: [
{path: '', redirectTo: 'tracks'},
{path: 'tracks', component: ArtistTrackListComponent},
{path: 'albums', component: ArtistAlbumListComponent},
]
}
Now when we try to navigate to the search view we are blocked from doing so and shown a window alert.
If we want to redirect users to a login page we may inject Router into the constructor and then use the navigate function to redirect them to the appropriate login page.
Note: So the rest of the samples in this chapter work we will change the isLoggedIn function on our UserService to return true instead.
CanActivateChild
As well as CanActivate we also have CanActivateChild which we implement in similar way.
Lets do the same as the CanActivate example and create a guard called AlwaysAuthChildrenGuard.
import {CanActivateChild} from "@angular/router";
class AlwaysAuthChildrenGuard implements CanActivateChild {
canActivateChild() {
console.log("AlwaysAuthChildrenGuard");
return true;
}
}
Note: Remember to
provide it on our NgModule
We add the guard to the canActivateChild child property on
our ArtistComponent route
{
path: 'artist/:artistId',
component: ArtistComponent,
canActivate: [OnlyLoggedInUsersGuard, AlwaysAuthGuard],
canActivateChild: [AlwaysAuthChildrenGuard],
children: [
{path: '', redirectTo: 'tracks'},
{path: 'tracks', component: ArtistTrackListComponent},
{path: 'albums', component: ArtistAlbumListComponent},
]
}
Now every-time we try to activate either the
ArtistTrackListComponent or ArtistAlbumListComponent child routes it checks the
AlwaysAuthChildrenGuard to see if the user has permission.
CanDeactivate:
Guard ask permission to discard unsaved changes.
0 comments:
Post a Comment