1

I have a route guard that needs to be configured. On some route we want to check if our user client is ready, on some others we want to check if our team client is ready etc.

I've my route guard that looks like this

@Injectable({
    providedIn: 'root'
})
export class ClientReadyGuard implements CanActivate, CanActivateChild {
    constructor(
        private clientName: string,
        private router: Router ,        
        private apolloState: ApolloStateService,

    ) {
        debugger;
    }

    canActivate(...) {
       // do things with clientName
    }

and from this guard I'd like to have multiple guards: one that protects with clientName 'all-users', one with 'user', one with 'team', and so on.

To have :

canActivate: [
   AllUserClientReadyGuard,
   UserClientReadyGuard,
   TeamClientReadyGuard,

]

To do so I tried with injection token with no luck; (NullInjectorError: No provider for InjectionToken router token!).

export const ROUTER_TOKEN = new InjectionToken<Router>('router token');
export const APOLLO_STATE_TOKEN = new InjectionToken<ApolloStateService>('apollo state token');


export const UserClientReadyGuard = new InjectionToken<ClientReadyGuard>(
    'user client ready guard',
    {
        providedIn: 'root', factory: () => new ClientReadyGuard(
            USER_CLIENT,
            inject(ROUTER_TOKEN),
            inject(APOLLO_STATE_TOKEN),
        )
    }
);
Ced
  • 15,847
  • 14
  • 87
  • 146

1 Answers1

0

I ended up using inheritance. Using { data } from the router is arguably a better option in general though. But since I ended up needing different behaviors for the guards extending allows me to override methods. I guess it's case dependent.

export abstract class ClientReadyGuard implements CanActivate, CanActivateChild {

    constructor(
        protected router: Router,
        protected apolloState: ApolloStateService,
        protected client: string
    ) { }

    canActivate(route: ActivatedRouteSnapshot): boolean | Observable<boolean> | Promise<boolean> {
        return this.checkReady(this.client);
    }


    protected checkReady(client: string): Observable<boolean> {
        //...
    }    
}

@Injectable({
    providedIn: 'root'
})
export class GlobalDataClientReadyGuard extends ClientReadyGuard {
    constructor(
        protected router: Router,
        protected apolloState: ApolloStateService,
    ) {
        super(router, apolloState, GLOBAL_DATA_CLIENT);
    }
}
Ced
  • 15,847
  • 14
  • 87
  • 146
  • I don't think using `data` is good at all, and I'm irritated that this is gaining traction as "best practice" in the angular community. `data` has two pitfalls specifically. 1) Multiple guards may wish to use the same property on the `data` object for different reasons, causing a conflict. 2) You can get no help in the form of auto-suggest or static checking from languages like TypeScript with the `data`. – crush Jun 06 '19 at 14:40