Angular 5:Service订阅变量未更新,路由守卫未执行Observable的map操作
Hey there! Let's break down what's going wrong here and fix it step by step.
The Core Issues in Your Code
- Cold Observable Behavior: Observables in RxJS are "cold" by default—meaning they won't execute unless you subscribe to them or return them to a system that will subscribe (like Angular's route guard system). In your current code, you call
this.usrData.isProfileComplete()but never subscribe to it, so themapoperator never runs. - Incorrect Return Type: The
canActivatemethod can return anObservable<boolean>,Promise<boolean>, or plainboolean. Your current logic mixes synchronous checks with unsubscribed asynchronous code, so Angular can't wait for the profile check to finish before deciding whether to activate the route. - Inefficient Data Fetch: You're using
list()to fetch all user data when you only need the singleprofileCompletefield. This adds unnecessary complexity and data transfer.
Step 1: Fix the UserDataService
Let's simplify the isProfileComplete method to fetch just the specific field we need:
import { map } from 'rxjs/operators'; constructor(private afd: AngularFireDatabase, private authService: AuthService) { } isProfileComplete(): Observable<boolean> { // Fetch only the profileComplete node instead of the entire user list return this.afd.object(`/users/${this.authService.uid}/profileComplete`) .valueChanges() .pipe( // Convert null/undefined to false (default to incomplete if field doesn't exist) map(completeStatus => !!completeStatus) ); }
Step 2: Rewrite the AuthGuard's canActivate Method
We need to return an Observable that handles the entire authentication + profile check flow, so Angular can properly wait for the result:
import { map, catchError } from 'rxjs/operators'; import { of } from 'rxjs'; constructor(private authService: AuthService, private router: Router, private usrData: UserDataService) {} canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> | boolean { // First check if user is authenticated if (!this.authService.isAuthenticated()) { this.router.navigate(['login']); console.log('user is not logged in '); return false; } // Return the profile check observable so Angular can subscribe to it return this.usrData.isProfileComplete().pipe( map(isComplete => { if (isComplete) { console.log('user is logged in & data form completed'); return true; } else { this.router.navigate(['data_form']); console.log('user logged in but not completed the form'); return false; } }), // Handle errors (e.g., user node doesn't exist) catchError(() => { this.router.navigate(['data_form']); return of(false); }) ); }
Key Explanations
- Returning the Observable: By returning the
isProfileComplete()stream (with operators), Angular automatically subscribes to it and waits for the result to decide route access. - Simpler Data Fetch: Using
object()instead oflist()lets us directly get theprofileCompletevalue, avoiding the need to loop through list items. - Error Handling: The
catchErroroperator ensures we handle any unexpected issues (like missing user data) by redirecting to the profile form.
内容的提问来源于stack exchange,提问作者sheeni
相关产品推荐
相关产品推荐

