I'm building an authentication app using the PEAN stack (i.e., PostgreSQL - ExpressJS - Angular - NodeJS).
Problem
I have a problem with my auth guard:
- If I use
subscribe()
, I get the expected result in the console. ✔ - WORKING - If I use
pipe()
, I getundefined
in the console. ✖ - NOT WORKING
if-signed-in.guard.ts
import { CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth/auth.service';
import { inject } from '@angular/core';
import { tap, map } from 'rxjs/operators';
import { Router } from '@angular/router';
export const IfSignedIn: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
auth.getSignInStatusObserver().subscribe((res: any) => {
console.log(res);
});
return auth.getSignInStatusObserver().pipe(
tap((status) => {
console.log(status);
}),
map((status) => {
console.log(status);
if (status) {
if (status.success === true) {
return true;
} else {
router.navigate(['/dashboard']);
return false;
}
} else {
router.navigate(['/dashboard']);
return false;
}
})
);
};
Screenshot:
What I've tried
I tried implementing the same logic inside subscribe()
:
if-signed-in.guard.ts
import { CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth/auth.service';
import { inject } from '@angular/core';
import { Router } from '@angular/router';
export const IfSignedIn: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
auth.getSignInStatusObserver().subscribe((res: any) => {
if (res) {
if (res.success === true) {
return true;
} else {
router.navigate(['/dashboard']);
return false;
}
} else {
router.navigate(['/dashboard']);
return false;
}
});
};
But I get the following error:
src/app/auth/guards/if-signed-in.guard.ts:7:14 - error TS2322: Type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => void' is not assignable to type 'CanActivateFn'. Type 'void' is not assignable to type 'boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>'.
7 export const IfSignedIn: CanActivateFn = (route, state) => {
Question
Why do I get undefined from the Observable in auth guard when using pipe()
, while subscribe()
works but I get an error?
EDIT 1
I've tried the code suggested by @MatthieuRiegler, but I'm still getting exactly the same response as in the screenshot above.
if-signed-in.guard.ts
import { CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth/auth.service';
import { inject } from '@angular/core';
import { filter, tap, map } from 'rxjs/operators';
import { Router } from '@angular/router';
export const IfSignedIn: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
auth.getSignInStatusObserver().subscribe((res: any) => {
console.log(res);
});
return auth.getSignInStatusObserver().pipe(
filter((status) => status !== undefined),
tap((status) => {
console.log(status);
}),
map((status) => {
console.log(status);
if (status) {
if (status.success === true) {
return true;
} else {
router.navigate(['/dashboard']);
return false;
}
} else {
router.navigate(['/dashboard']);
return false;
}
})
);
};
EDIT 2
My app behaves strangely. Now I have the following code:
if-signed-in.guard.ts
import { CanActivateFn } from '@angular/router';
import { AuthService } from '../services/auth/auth.service';
import { inject } from '@angular/core';
import { filter, tap, map } from 'rxjs/operators';
import { Router } from '@angular/router';
export const IfSignedIn: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.getSignInStatusObserver().pipe(
filter((status) => status !== undefined),
tap((status) => {
console.log('Tap: ', status);
}),
map((status) => {
console.log('Map: ', status);
if (status) {
if (status.success === true) {
return true;
} else {
router.navigate(['/dashboard']);
return false;
}
} else {
router.navigate(['/dashboard']);
return false;
}
})
);
};
If I click the Edit profile button, everything works as expected.
If I type in the search bar http://localhost:4200/profile/edit-profile
, it looks like something is wrong with emitting values because I receive undefined
, and because the filter()
works, I don't get anything in the console.
Also, the same thing happens on page refresh. It seems like the auth guard isn't working on page refresh.
Screenshot:
EDIT 3
I use an interceptor to set sign-in status as follows:
sign-in-status.service.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { AuthService } from 'src/app/auth/services/auth/auth.service';
@Injectable({
providedIn: 'root',
})
export class SignInStatusService implements HttpInterceptor {
intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(httpRequest).pipe(
tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse && httpRequest.url.endsWith('api/get-user') && event.status === 200) {
this.authService.setSignInStatus({ success: true, response: event.body });
}
}),
catchError((err: any) => {
if (httpRequest.url.endsWith('api/get-user')) {
this.authService.setSignInStatus({ success: false, response: err });
}
return throwError(() => err);
})
);
}
constructor(private authService: AuthService) {}
}