Using Angular2 to create a single page app, I\'m intercepting unauthenticated user access to non-public routes in a custom RouterOutlet
and redirecting them to
Updated Example Using Angular 2.2.1
Auth Guard that passes original URL to login component:
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (localStorage.getItem('currentUser')) {
// logged in so return true
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login', { returnUrl: state.url }]);
return false;
}
}
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AlertService, AuthenticationService } from '../_services/index';
@Component({
moduleId: module.id,
templateUrl: 'login.component.html'
})
Login Component that redirects to the previous / original URL after login:
export class LoginComponent implements OnInit {
model: any = {};
loading = false;
returnUrl: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private alertService: AlertService) { }
ngOnInit() {
// reset login status
this.authenticationService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.params['returnUrl'] || '/';
}
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(
data => {
// login successful so redirect to return url
this.router.navigate([this.returnUrl]);
},
error => {
// login failed so display error
this.alertService.error(error);
this.loading = false;
});
}
}
For more details and working demo you can check out this post
This worked for me. Inject it into your main App component's constructor having registered it in the bootstrap method.
The first val
on page load should be the original URL.
I am unsubscribing right after to be efficient since I don't (perhaps, yet) want to listen to subsequent router events in this service.
Inject the service into other places where originalUrl is needed.
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
@Injectable()
export class UrlTrackingService {
public originalUrl: string;
constructor(
private router: Router
) {
let subscription: Subscription = this.router.events.subscribe((val) => {
this.originalUrl = val.url;
subscription.unsubscribe();
});
}
}
You might find what you need in the docs for the Location
class. The back()
function could possibly do it for you.
Another approach would be to subscribe to the popstate
events in Location
, instead. MDN has docs talking about the values you could expect to receive.
class MyCLass {
constructor(private location: Location) {
location.subscribe((val) => {
// do something with the state that's passed in
})
}
}
Otherwise you might want a service that tracks the changes in the Router so that you can access them.
class MyTrackingService {
constructor(private router: Router) {
router.subscribe((val) => {
// store the routes here so that you can peel them off as needed?
})
}
}
In this case I'm accessing the Router
object and subscribing to any changes so that I could track them.
Use RouterStateSnapshot in the auth guard to capture the requested URL.
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
{
// keep the attempted URL for redirecting
this._loginService.redirectUrl = state.url;
}
Redirect to that URL on successful authentication with using Router (e.g. in the login.component.ts
). E.g. this._router.navigateByUrl(redirectUrl);
P.S. Suggestions of @MichaelOryl and @Vitali would work, but my way is more aligned with Angular2 final release.