Search code examples
javascriptangulartypescriptgoogle-cloud-firestorescoping

Angular / Typescript / Firestore - How to return an observable value within an if statement


I am using angular and firestore. I'm checking firestore for a value inside my route guard.

I have found that on page refresh, it returns as undefined. However, if I just hardcode a return of true or false, it works.

Any logs within my if statement always return correctly, but doesn't seem to be updating my global variable for some reason.

If I use my site navigation to get back to the root and navigate through my site, it works correctly. However, when I refresh the page, it returns as undefined.

Could it be a scoping issue?

Route Guard

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Subscription } from 'rxjs/Subscription';


//testing
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';

@Injectable()
export class CheckBillingService implements CanActivate {
    private _subscription: Subscription;
    private userBillingDocRef: AngularFirestoreDocument<any>;
    userBilling: Observable<any>;
    public activeAccount:Observable<boolean>

    constructor(private authService: AuthService,
                private router: Router,
                private auth: AngularFireAuth,
                private readonly afs: AngularFirestore) {}

    canActivate(): Observable<boolean> {
        this.authService.user.subscribe((user) => {
            if (user) {
                var userId = user.uid;

                this.userBillingDocRef = this.afs.doc('user_billing/${userId}');

                this.userBilling = this.userBillingDocRef.snapshotChanges();
                this.userBilling.subscribe((value) => {

                    const data = value.payload.data();

                    if (data.account_status == "Active") {
                        this.activeAccount = Observable.of(true);
                        console.log('inside if statement for active = ', this.activeAccount);
                    } else {
                        this.activeAccount = Observable.of(false);
                        console.log('inside if statement for not active = ', this.activeAccount);
                        this.router.navigate(['resubscribe']);
                        return Observable.of(false);
                    }

                    console.log('userBilling.subscribe = ', this.activeAccount);
                });

                console.log('just outside userBilling.subscribe = ', this.activeAccount);
            }
        });

        // When refreshig my page, this returns as undefined.
        // If I navigate to the correct page and work my way through the site it works fine
        // However, refresh returns undefined.

        console.log('out of auth = ', this.activeAccount);

        return this.activeAccount;
    }
}

Solution

  • According to your code the page will run before your observable method is returned as it's running asynchronously, instead return the full method as observable like so

     canActivate(): Observable<boolean> {
    
        return Observable.create(observer=> {
    this.authService.user.subscribe((user) => {
    
                    if (user) {
    
                        var userId = user.uid;
    
                        this.userBillingDocRef = this.afs.doc('user_billing/${userId}');
    
                        this.userBilling = this.userBillingDocRef.snapshotChanges();
                        this.userBilling.subscribe((value) => {
    
                            const data = value.payload.data();
    
                            if (data.account_status == "Active") {
                                this.activeAccount = Observable.of(true);
     // observe here 
            observer.next(true)
                                console.log('inside if statement for active = ', this.activeAccount);
                            } else {
                                this.activeAccount = Observable.of(false);
              // observe here 
            observer.next(false)
                                console.log('inside if statement for not active = ', this.activeAccount);
                                this.router.navigate(['resubscribe']);
                            }
    
                            console.log('userBilling.subscribe = ', this.activeAccount);
                        });
    
                        console.log('just outside userBilling.subscribe = ', this.activeAccount);
    
                    }
    
                });
        });
    
    
                // When refreshig my page, this returns as undefined.
                // If I navigate to the correct page and work my way through the site it works fine
                // However, refresh returns undefined.
    
                console.log('out of auth = ', this.activeAccount);
    
            }
    

    Observe where I wrapped everything in Observer.create which you can learn more here https://stackoverflow.com/a/44334611/5836034

    then observer.next will return what you really want the canActivate hook to react on

      // observe here 
            observer.next(true)
        observer.next(false)