首页 > 解决方案 > How do I redirect to different home page when user is logged in Angular?

问题描述

I am trying to make a redirection to the /dashboard page whenever user is logged in. The default landing page is /home, but logged in user home page is /dashboard.

I have a user service class where I create a sharedUser and it is BehaviorSubject:

  public sharedUser= new BehaviorSubject<User>(null);

And then I have initUser method, where I give values to user object. initUserStatus() method is Observable and fills data from request.

private initUser(accessToken: string): void {
    const tokenData = TokenUtil.decodeToken(accessToken);

    this.user.email = tokenData.email;
    this.initUserStatus().subscribe(
      () => this.sharedUser.next(this.user)
    );
  }

And then I have Guard class where I try to redirect if user data is filled and is actually logged in: User is logged in when email is initalized, isLoggedIn() method checks if there is email or not.

export class HomePageGuard implements CanActivate {

  constructor(private userService: UserService, private router: Router) { }

  public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<UrlTree | boolean>  {
    return this.userService.sharedUser.asObservable().pipe(
      map(() => {
        if (this.userService.isLoggedIn()) {
          return this.router.createUrlTree(['/dashboard']);
        }
        return true;
      }
    ));
  }
}

It seems that canActivate will be in endless loop and it won't go complete and thus it will not redirect to /dashboard page.

How can I fix that? Or what am I missing?

标签: angularangular9behaviorsubject

解决方案


The observable returned from a guard does not have to complete. Angular will use the first() operator anyways.

const canActivateObservables = canActivate.map((c: any) => {
    return defer(() => {
      const guard = getToken(c, futureARS, moduleInjector);
      let observable;
      if (isCanActivate(guard)) {
        observable = wrapIntoObservable(guard.canActivate(futureARS, futureRSS));
      } else if (isFunction<CanActivateFn>(guard)) {
        observable = wrapIntoObservable(guard(futureARS, futureRSS));
      } else {
        throw new Error('Invalid CanActivate guard');
      }
      return observable.pipe(first()); // 
    });
  });

What you could do in order to avoid the infinite loop is to check in your guard if the URL you're about to navigate to contains the /dashboard path. If that's the case, then you should not schedule a new navigation and return true instead.

 map(() => {
  
  // or 
  // this.userService.isLoggedIn() && !route.url.some(({ path }) => path === 'dashboard')
  if (this.userService.isLoggedIn() && !state.url.includes('dashboard')) {
    return this.router.createUrlTree(['/dashboard']);
  }
  return true;
}

推荐阅读