Below are three different methods to achieve the desired outcome, ranked from least preferred to most preferred:
Option 1. Implement imperative redirection in AppComponent
@Component({
selector: 'app-root',
template: `...`
})
export class AppComponent {
constructor(authService: AuthService, router: Router) {
if (authService.isLoggedIn()) {
router.navigate(['dashboard']);
}
}
}
This method is not recommended as it's better to define the "login required" information within the route declaration.
Option 2. Utilize a CanActivate
guard
Add a CanActivate
guard to all routes that necessitate user login:
const APPROUTES: Routes = [
{path: 'home', component: AppComponent, canActivate:[LoginActivate]},
{path: 'dashboard', component: DashBoardComponent, canActivate:[LoginActivate]},
{path: 'login', component: LoginComponent},
{path: '**', component: NotFoundComponent}
];
The guard used here is named LoginActivate
.
To make this guard functional, it needs to be added to the module's providers
and properly implemented. In this scenario, the guard can redirect users if they are not logged in:
@Injectable()
export class LoginActivate implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean>|Promise<boolean>|boolean {
if (!this.authService.isLoggedIn()) {
this.router.navigate(['login']);
}
return true;
}
}
If this seems confusing, refer to the documentation on route guards at: https://angular.io/docs/ts/latest/guide/router.html#guards
Although this option is an improvement, it may lack flexibility when handling conditions beyond just user login status or requiring additional parameters like user roles.
Option 3. Leverage the route data
property
The optimal solution involves adding metadata in route declarations indicating that the user must be logged in.
This can be achieved using the route's data
property, with a flag such as requiresLogin
set to either true
or false
by default:
const APPROUTES: Routes = [
{path: 'home', component: AppComponent, data:{requiresLogin: true}},
{path: 'dashboard', component: DashBoardComponent, data:{requiresLogin: true}}
];
While the data
property alone doesn't trigger any action, it helps enforce the "login required" logic by utilizing a CanActivate
guard accordingly. This may require attaching both metadata and the guard to each protected route.
However, you can streamline this process by applying the CanActivate
guard to a top-level route, impacting all subsequent child routes. This approach reduces redundancy and offers versatility through customizable parameters provided via the data
property.
In light of these considerations, renaming the guard to a more generic term like AccessGuard
could prove beneficial.
I'll present a snippet focusing on how the guard accesses the attached data
within the route, as further actions inside the guard would vary based on specific requirements:
@Injectable()
export class AccessGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot): Observable<boolean>|Promise<boolean>|boolean {
const requiresLogin = route.data.requiresLogin || false;
if (requiresLogin) {
// Logic to verify user login status...
}
}
}
To implement the above code effectively, ensure your route setup resembles something similar to:
{
path: 'home',
component: AppComponent,
data: { requiresLogin: true },
canActivate: [ AccessGuard ]
}
Note: Remember to include AccessGuard
in your module's list of providers
.