简体   繁体   中英

this.router.navigate in Guard blocks routes in future

I have set up two guards in Angular 4 -- one that redirects users to the login page when they try to reach a protected route, and one that redirects users to the welcome page from 'Home' if they haven't been there yet.

The guards themselves work beautifully... but I've noticed some very strange behavior. Adding redirection through this.router.navigate in the WelcomeTraveler guard puts the app in a state where I can't access the protected routes from the first guard, even after logging in. I just keep getting sent back to the home page.

Here are my guards:

export class AuthGuardLoggedInUser implements CanActivate {
  private isLoggedIn: boolean;
  private working: boolean;
  constructor (@Inject(Store) private _store:Store<AppStore>, @Inject(Router) private _router: Router) 
  {
    _store.select(state => state.AuthNState).subscribe(auth =>
    {
      this.isLoggedIn = auth.connected
      this.working = auth.working
    })
  }
  canActivate() {
    if (this.working)
    {
      let promise: Promise<boolean>  = new Promise((resolve, reject) => {
        let sub = this._store.select(state => state.AuthNState).subscribe(auth =>
        {
          if (!auth.working) {
            resolve(auth.connected)
            sub.unsubscribe()
            if (!auth.connected) this._router.navigate(['/i/login']);
          }
        })
      });
      return promise
    }
    else if (this.isLoggedIn){
      return true
    }
    else {
      this._router.navigate(['/i/login']);
    }

export class WelcomeTraveler implements CanActivate {
  private hasAlreadyVisitedWelcomePage: boolean;
  private isLoggedIn: boolean;
  private working: boolean;
  constructor (@Inject(Store) private _store:Store<AppStore>, @Inject(Router) private _router: Router) 
  {
    _store.select(state => state.AuthNState).subscribe(auth =>
    {
      this.isLoggedIn = auth.connected
      this.working = auth.working
    })
  }
  canActivate() {
    if (this.working)
    {
      let promise: Promise<boolean> = new Promise((resolve, reject) => {
        let sub = this._store.select(state => state.AuthNState).subscribe(auth =>
        {
          if (!auth.working) {
            resolve(auth.connected)
            sub.unsubscribe()
            this.hasAlreadyVisitedWelcomePage = true
            this._router.navigate(['/i/welcome']);
          }
        })
      });
      return promise
    }
    else if (this.isLoggedIn){
      return true
    }
    else if (!this.hasAlreadyVisitedWelcomePage){
      this.hasAlreadyVisitedWelcomePage = true
      this._router.navigate(['/i/welcome']);
    }
    else return true
  }
}

And here's a snippet of the routing table:

export var AppRoutes = RouterModule.forRoot([
  {
    path: '',
    component: HomeComponent,
    canActivate: [WelcomeTraveler]
  }, {
    path: 'i/getstarted',
    component: GetStartedPageComponent,
    canActivate: [AuthGuardLoggedInUser]
  }, {
    path: 'i/login',
    component: LoginPageComponent
  }, {
    path: 'i/profile',
    component: ProfilePageComponent,
    canActivate: [AuthGuardLoggedInUser]
  }, {
    path: 'i/welcome',
    component: WelcomePageComponent
  }])

The very presence of this.router.navigate in the WelcomeTraveler guard seems to cause the problem, even if those lines are never hit! Once logged in, I get sent back to 'Home' immediately after trying to route to the Profile (after successfully making it through the first guard). If I remove the navigate lines -- the issue goes away.

Any ideas?

As so often happens, I was on the wrong track here. For those who might have starred or up-voted this, I would advise you to check any subscriptions you might have that invoke a router.navigate -- in my case, I was failing to clean up these subscriptions on my login/signup components... so once the subscription was initialized, any time the state updated I was redirected to the home page.

Fixed like so:

export class LoginPageComponent implements OnDestroy {
   private _redirectSubscription: Subscription;
   constructor (private _store:Store<AppStore>, private router: Router) {
      this._redirectSubscription = _store.select((state) => state.AuthNState).subscribe((auth) =>
      {
        if (auth.connected) this.router.navigate(['']);
      })
   }

   ngOnDestroy() {
     //Called once, before the instance is destroyed.
     //Add 'implements OnDestroy' to the class.
     this._redirectSubscription.unsubscribe();
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM