Home > Net >  How can I get an angular route guard, or the service it calls, to wait for a response before evaluat
How can I get an angular route guard, or the service it calls, to wait for a response before evaluat

Time:04-23

I'm trying to implement some basic logic in a route guard to verify that a user is authorized to access a URL.

When a user clicks on a link from a list, I'm grabbing the firestore document ID and passing that ID as a URL parameter to the next page. With this method, the URL shows the document ID so users could change IDs to something they didn't have access to.

The route guard I wrote worked... mostly. The route guard unfortunately fails the first time through and kicks you back to the home page, but if you log back in then it works. After setting some breakpoints and debugging, I could tell that the code that evaluates the IDs and returns the boolean wasn't waiting for the database call for the document user ID.

I'm assuming I need to implement some sort of observable, but I'm not sure at which point of the process I should emit the observable, and where I should subscribe (service, or guard?). I started with the assumption I should have some of the calls in a service, but maybe it would be better to have them in the guard itself? I appreciate any help.

Route Guard

canActivate(route: ActivatedRouteSnapshot): boolean {
    this.pid = route.paramMap.get('pid')
    if (this.authService.isPartyAuth(this.pid) !== true) {
      this.router.navigate(['home'])
    }
    return true;
  }

auth service

liveUid is getting set in the constructor, as well as partyCollectionRef

isPartyAuth(pid: string) {

    this.partyCollectionRef.doc(pid).ref.get().then((doc) => {
      if (doc.exists) {
        this.partyUserId = doc.data().uid
      } else {
        console.log("No such document!");
      }
    }).catch(function (error) {
      console.log("Error getting document:", error);
    });
    
    return this.partyUserId == this.liveUid && this.partyUserId !== undefined && this.liveUid !== undefined ? true : false;
  }

CodePudding user response:

You cannot return anything from subscribe, use map instead, also you need to add a return statement.

canActivate() {
  // add 'return'
  return this.authService.isPartyAuth(this.pid)
   // change this!
   // .subscribe(resp=> {
   .map(resp=> {
     if(!resp) {
        this.router.navigate(['home'])
        return false;
     }
     return true;
   }); 
 }

CodePudding user response:

I made a couple of minor tweaks and got this working:

Route Guard

I made the whole function async, and added an await to the call to the service.

  async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {

    this.pid = route.paramMap.get('pid')

    if (await this.authService.isPartyAuth(this.pid) !== true) {

      this.router.navigate(['home'])

    }
    
    return true;
  }

}

Auth Service

I also updated the auth service to return the boolean after I have waited for, and gotten the document data.

isPartyAuth(pid: string) {

   return this.partyCollectionRef.doc(pid).ref.get().then((doc) => {
      if (doc.exists) {
        this.partyUserId = doc.data().uid
        return this.partyUserId == this.liveUid && this.partyUserId !== undefined && this.liveUid !== undefined ? true : false;
      } else {
        console.log("No such document!");
        return false
      }
    }).catch(function (error) {
      console.log("Error getting document:", error);
    });


  }
  • Related