0

I'm using Ionic with Angular. Inside one of my route guards, i need to see if a firestore document is created or not and the return a boolean value for it and this takes some time to get the response from the server however the route guard does not wait for the value to return and runs normally, so the page is not protected! How can i make the route guard wait for the value? or, return the value before the route guard runs? this is where i send the request to firestore:

export class FirebaseService {

  ex: Promise<boolean>;
  categoryDone: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
  constructor(private afs: AngularFirestore) {
    this.ex = new Promise((resolve) => {
          this.afs.collection('users').doc(this.user.uid).collection('categories').get().toPromise().then(
            data => {
              if (data.size > 0) {
                resolve(true)
              } else {
                resolve(false)
              }
            }
          )
        })
        this.ex.then(data=>{
          if (data) {
            this.categoryDone.next(true)
          } else {
            this.categoryDone.next(false)
          }
        })
  }

And this is my route guard code where i use it for my page:

export class CategoryDoneGuard implements CanActivate {
  categoryDone: boolean;
  constructor(private router: Router, private firebaseService: FirebaseService) {
    this.firebaseService.categoryDone.asObservable().subscribe(
      categoryDone => this.categoryDone = categoryDone
    )
  }
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | boolean {
      console.log(this.categoryDone,"Category Done VAriable in CategoryDoneGuard")
      setTimeout(() => {
        console.log(this.categoryDone,"Catgeory") //true because i waited 1 second
      }, 1000);
    if (!this.categoryDone) { //variable is undefined here 
      console.log('You are in InfoDoneGuard')
      return true
    }
    this.router.navigate(['tabs/tab-profile'])
    return false
  }

}
Shakar Bakr
  • 57
  • 1
  • 10
  • Off-topic... but why are you using `toPromise()` and also creating a new Promise? Anyway, I suggest you embrace rxjs, it's so versatile and totally worth learning! :) – AT82 Nov 16 '19 at 19:14
  • Do you have any idea how to fix it? – Shakar Bakr Nov 16 '19 at 19:17

2 Answers2

2

Just like in other answer, auth guard can accept an boolean, observable or promise of boolean or urltree. But I would restructure that whole service function a bit, unless you need categoryDone for something else, but you can add that anyway in your service if you need it.

But as your service currently sits, you are calling toPromise() but also creating a new promise, which doesn't really make sense.

I would do...

Service:

// please choose a more suitable name :D 
getBool() {
  return this.afs.collection('users').doc(this.user.uid).collection('categories').get().pipe(
    map(data => data.size > 0),
    // if you need
    // tap(bool => this.categoryDone.next(bool))
  )
}

Can Activate:

canActivate(...): Observable<boolean> {
  return this.firebaseService.getBool().pipe(
    map(bool => {
      if (bool) {
        return true;
      }
      this.router.navigate(['tabs/tab-profile'])
      return false;
    })
  )
}

But if categoryDone is essential for you in your app, you can also subscribe to it in the authguard like in MoxxiManagarm's answer. Anyhow I would restructure the firebase query to remove the promise stuff.

AT82
  • 71,416
  • 24
  • 140
  • 167
  • But i need to merge to observables for example i also need to know whether i'm logged in or not how can i merge those two and then return false or true and route to other pages? – Shakar Bakr Nov 16 '19 at 20:03
  • What does authentication have to do with this question? I see nowhere any code of authentication? – AT82 Nov 17 '19 at 07:53
  • First, i have to know if the user is logged in or not then i have to know if he finished the setup(category). user has to choose the categories he/she likes to see the posts of those categories. So, check if logged in first. then check if he finished setting up if those two were true then navigate to home(tabs/tab-profile)page. I would appreciate any help – Shakar Bakr Nov 17 '19 at 10:42
  • Well you are not showing any of that code, so how are we supposed to help ;) But you can chain any requests with `switchMap` if they need to be executed in order, else use `forkJoin`.https://www.learnrxjs.io/operators/combination/forkjoin.html and https://www.learnrxjs.io/operators/transformation/switchmap.html – AT82 Nov 17 '19 at 10:55
0

Don't return the boolean, return the Observable!

export class CategoryDoneGuard implements CanActivate {

      constructor(
        private router: Router,
        private firebaseService: FirebaseService
      ) { }

      canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot,
      ): Observable<boolean> {

        return this.firebaseService.categoryDone.asObservable().pipe(
          map(Boolean),
          tap(b => if (!b) this.router.navigate(['tabs/tab-profile'] ),
        );
     }
MoxxiManagarm
  • 8,735
  • 3
  • 14
  • 43