1

I actually got multiple guard which share the same exact data called from my API.

My guards look like that :

export class GuardBlockAgent implements CanActivateChild {
 ...

 canActivateChild(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ): Observable<boolean> | boolean {

        return this.userService.retrieveMyUser(null, environment.liveMode).map(
        ...
    )}

I am looking for a way to share my data between my guards. Instead of making multiple call for the same data.

In this post they use this way with canActivate

{ 
  path: 'super-user-stuff', 
  component: SuperUserStuffComponent,
  canActivate: RoleGuard,
  data: {roles: ['SuperAdmin', ...]}
}

But how to pass dynamic data which comes from my API ?

theme-routing.module.ts

const routes: Routes = [
    {
        "path": "",
        "component": ThemeComponent,
        "resolve": { me: AgentResolver },
        "canActivate": [AuthGuard],
        "children": [
            {
                "path": "...",
                "loadChildren": "...",
                "canActivateChild": [GuardBlockAgent]
            },
];

@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class ThemeRoutingModule { }

EDIT: localStroage are not safe, that's why I prefer not to use it.

crg
  • 4,284
  • 2
  • 29
  • 57

1 Answers1

2

You can create a facade layer for your UserService. The layer stores your user for runtime and it will not send requests to the server if data exist.

Check the stackBlitz example. (Check console)

To create it;

Step 1: Create a facade service.

user-service.facade.ts

@Injectable({
  providedIn: 'root'
})
export class UserServiceFacade extends UserService {
  private readonly user$ = new BehaviorSubject<{ name: string } | null>(null);

  retrieveMyUser(): Observable<{ name: string }> {
    return this.user$.pipe(
      startWith(this.user$.value),
      switchMap(user => (user ? of(user) : this.getUserFromServer())),
      take(1)
    );
  }

  private getUserFromServer() {
    return super.retrieveMyUser().pipe(tap(user => this.storeUser(user)));
  }

  private storeUser(user: { name: string }): void {
    this.user$.next(user);
  }
}

Step 2: Override the dependency injection of the UserService token.

app.module.ts or wherever you provided UserService

  providers: [
    {
      provide: UserService,
      useClass: UserServiceFacade
    }
  ],

And it is done!

nevzatopcu
  • 1,049
  • 1
  • 7
  • 13
  • Thanks for your answer. I do not see how it can help me sharing data between my guards. Maybe I didn't get how you use it. If you may show me how it would work in a guard it would be nice. I've done what you did in the stackBlitz but it doesn't change anything... I still have multiple call when using my guards – crg Jun 02 '21 at 08:50
  • Actually in your exemple on stackBlitz the UserServiceFacade is not needed to make it work. It seems that your entire UserServiceFacade is useless ... – crg Jun 02 '21 at 08:56
  • It is storing data and not sending requests if the user already exists. And this means you can call the `retrieveMyUser` method everywhere you want. If the user exists it returns the user immediately, If not, it sends a request and stores user data. In `stackblitz` example, you can check the console. It is logging while sending a request to retrieve the user. @crg Moreover, if you need a more complicated solution, you can check `state management` libraries like ngrx or ngxs etc. – nevzatopcu Jun 02 '21 at 09:33
  • Seems like I was doing something the wrong way, it works fine. When I get to a new page and call the guard it does not make the call again. Thanks again for your help ! – crg Jun 02 '21 at 11:48