Is there any way we can redirect to a different component from @CanActivate in Angular2 ?
-
2Please change the accepted answer to @superjos's answer, instead of the outdated answer from Jk Jensen – Mark Lagendijk Feb 07 '17 at 13:06
6 Answers
As of today, with latest @angular/router 3.0.0-rc.1, here are a couple of references on how to do that through CanActivate
guards on routes:
- angular 2 reference
- Two answers to this SO question, by Nilz11 and by Jason
The main gist of logic looks like:
// ...
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.authService.isLoggedIn) {
// all ok, proceed navigation to routed component
return true;
}
else {
// start a new navigation to redirect to login page
this.router.navigate(['/login']);
// abort current navigation
return false;
}
}
-
5
-
would love to see how the redirect to the intended path is done after user logs using this auth guard. – Dawit Jul 14 '17 at 04:15
-
@Davvit I'm not sure what to do with proposed edit: by just adding a parameter to a call we're not providing much more information. Wouldn't be more helpful to me and others if you add a new answer, where you put all the additions and changes to satisfy your query? What do you think? – superjos Jul 14 '17 at 09:22
Your guard can easily just be an injectable which, as such, can include its own injectables. So we can simply inject the router, in order to redirect. Don't forget to add the service as a provider in your app module.
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (!authService.isAuthenticated()) {
this.router.navigate(['/login']);
return false;
}
return true;
}
}
export const ROUTES: Routes = [
{path: 'login', component: LoginComponent},
{path: 'protected', loadChildren: 'DashboardComponent', canActivate: [AuthGuard]}
];

- 37,253
- 15
- 92
- 74
Yes, you can! Doing this will prevent your component from instantiating for nothing.
First, make a new file src/app-injector.ts
let appInjectorRef;
export const appInjector:any = (injector = false) => {
if (!injector) {
return appInjectorRef;
}
appInjectorRef = injector;
return appInjectorRef;
};
Then, get the reference from bootstrap
// ...
import {appInjector} from './app-injector';
// ...
bootstrap(App, [
ROUTER_PROVIDERS
]).then((appRef) => appInjector(appRef.injector));
Finally in your function
// ...
import {appInjector} from './app-injector';
// ...
@CanActivate((next, prev) => {
let injector = appInjector();
let router = injector.get(Router);
if (42 != 4 + 2) {
router.navigate(['You', 'Shall', 'Not', 'Pass']);
return false;
}
return true;
})
Et voilà !
It was discussed here https://github.com/angular/angular/issues/4112
You can find a complete example here http://plnkr.co/edit/siMNH53PCuvUBRLk6suc?p=preview by @brandonroberts
-
1Edited your answer (if approved): `export const appInject:any` otherwise errors will be thrown on typescript compile. – malifa Apr 28 '16 at 12:43
-
Please note this answer is no longer relevant in later Angular versions. – Dane Brouwer Oct 20 '20 at 19:42
As of Angular 7.1, you can return UrlTree
instead of boolean
:
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) { }
canActivate(): boolean | UrlTree {
return this.authService.isAuthenticated() || this.router.createUrlTree(['/login']);
}
}

- 81
- 1
- 1
This might help someone who is trying to wait on something before continuing.
waitForBonusToLoad(): Observable<[boolean, string]> {
const userClassBonusLoaded$ = this.store.select(fromRoot.getUserClasssBonusLoaded);
const userClassSelected$ = this.store.select(fromRoot.getClassAttendancesSelectedId);
return userClassBonusLoaded$.withLatestFrom(userClassSelected$)
.do(([val, val2]) => {
if (val === null && val2 !== null) {
this.store.dispatch(new userClassBonus.LoadAction(val2));
}
})
.filter(([val, val2]) => {
if(val === null && val2 === null) return true;
else return val;
})
.map(val => {
return val;
})
.take(1);
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.waitForBonusToLoad().map(([val, val2]) =>
{
if(val === null && val2 === null){
this.router.navigate(['/portal/user-bio']);
return false;
}
else {
return true;
}
}
)
}

- 14,999
- 9
- 67
- 78
-
Your proposed solution contains side effects within Observable.map() Instead, you should move the navigation out of map() and into do(). Your map should simply resolve TRUE/FALSE. Observable$.map((x) => x === 1).do(result => { if (result) navigate(); }); – josh-sachs Apr 04 '17 at 20:39
If you are using an observable to determine, you can leverage tap
operator from rxjs
:
@Injectable({
providedIn: SharedModule // or whatever is the equivalent in your app
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService,
private router: Router) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authService.isLoggedIn()
.pipe(tap((isLoggedIn: boolean) => {
if (!isLoggedIn) {
this.router.navigate(['/']);
}
}));
}
}

- 12,579
- 9
- 44
- 72