angularserviceguard

angular guard-service inject service


I need to inject a service in a guard. This guard checks if the user was invited, when yes, he can access the route.

To check this condition, I need to call a service which fetches this information from the db.

I have a cyclical dependency error, I understand that we shouldn't inject services in Guards, but in this case, I need to do it:

  providers: [AuthService, HackService, HacksStorageService, AuthGuard, EmailGuard],

And the guard:

import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate } from "../../../node_modules/@angular/router";
import { HacksStorageService } from "../shared/hacks-storage.service";

export class EmailGuard implements CanActivate {

  constructor(
    private hacksStorageService: HacksStorageService,
  ) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    this.hacksStorageService.fetchHack();
    // check if user was invited. should be in the participants array
    return true;
  }
}

I am pretty confused. Usually I used guards to see if user is logged in or not, so I usually imported stuff from firebase, not from my own services, so not cyclical dependencies.

Now I want to check if a condition happens, based on my own data. How can I inject my own data in the EmailGuard, if I am not allowed to inject services because of the cyclical dependency?

Thanks.


Solution

  • You can inject services in guards. If your service returns synchronously, then you can just return immediately, like in your sample code. Otherwise, I did it like this (using firebase auth)

    import { Injectable } from '@angular/core';
    import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
    import { Observable, of } from 'rxjs';
    import { map, first } from 'rxjs/operators';
    import { AngularFireAuth } from '@angular/fire/auth';
    import { Paths } from './paths';
    
    @Injectable({
      providedIn: 'root'
    })
    export class SignInGuard implements CanActivate {
    
      constructor(private afAuth: AngularFireAuth, private router: Router) { }
    
      canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
          return this.afAuth.user.pipe(
            first(),
            map(user => {
              if (user) {
                this.router.navigate([Paths.MAIN_PATH]);
                return false;
              } else {
                return true;
              }
            })
          );
      }
    }