Angular 17 route guard using Signal not working

1.4k views Asked by At

I'm writing a route guard function using a signal from an auth service:

export const canActivate: CanActivateFn = ()=> {
  console.log('guard called');
  const authService = inject(HttpAuthService);
  const router = inject(Router);
  const loggedIn = computed<boolean>(() => {
    const result = authService.user() !== null;
    console.log('In computed: '+ result);
    return result;
  });

  if (loggedIn()) {
    console.log('can activate true');
    return true;
  } else {
    console.log('can activate false');
    return router.navigateByUrl('login');
  }
};

The guard get's called but always return true even when the user is null after logout, Implementation of logout logs the new userState as expected null:

  logout(): void {
    this.userSignal.set(null);
    console.log('Logout: '+ this.user())
  }

So invoking logout logs "Logout: null" as expected;

but the guard always navigate to the login page. Logging shows that the computed block always return false despite the fact that the user signal from the auth service is updated on login and logout. I know this because the logout button is activated on successful login and disappear after clicking the logout button. This is the logout code:

@Component({
  selector: 'app-logout',
  standalone: true,
  imports: [MatButtonModule, MatIconModule, NgIf],
  template: `
    <ng-container *ngIf="user()">
        <button mat-icon-button (click)="onLogout()" >
            <mat-icon>logout</mat-icon>
        </button>
    </ng-container>

  `,
  styles: ``
})
export class LogoutComponent {
  user;
  constructor(private authService: HttpAuthService) {
    this.user = authService.user
  }
  onLogout() {
    this.authService.logout();
  }
}

So it looks like my guard is wrong, what am I missing? I also tryed to wrap the computed signal into an Observable with toObservable but that didn't change the result.

2

There are 2 answers

1
Oleksii Yurchenko On

The guard get's called but always return true even when the user is null

after logout Logging shows that the computed block always return false

These assertions contradict with each other. But anyway login/logout are async, therefore, your guard may not have the actual user context by the time it is executed. Have you tried to return from the guard something like this?

toObservable(authService.user).pipe(
  map(user => {
    if (user) return true;
    else return router.createUrlTree(['/login']);
  })
);
0
Ben Ooms On

Well, after trying multiple variants of my guard and even refactoring the auth service with the rx based state using BehaviorSubject and Observable it turned out that the fix was in the login component by just calling router.navigateByUrl.