0

I am using the @ng-idle/core package to implement an idle timeout in my angular 2 application (at angular 14 currently). On exceeding the idle timeout, the code logs the user out. The idle detection and logging out functionality has been written into my app.component.ts file.

When written with a hard-coded value for idle time, the code works perfectly. However, I would like the idle time to be configurable on my backend. Hence I have written a service class that fetches that value from the backend on application load. The rest of the code is exactly the same as the hard-coded idle time version (except that it is now inside the subscribe block of my service subscription). However, the ng-idle subscriptions never fire. The code is as follows:

    constructor(
        private router: Router,
        private idle: Idle, cd: ChangeDetectorRef,
        private authSvc: AuthService,
        private sessionDataSvc: SessionDataService,
        private countdownSvc: CountdownService
      ) {

    // set idle parameters
    this.sessionDataSvc.getSessionProperties().subscribe(( {data} ) => {
      let session_timeout = data.sessionProperties.session_idle_timeout
      // set the idle and timeout values
      // let session_timeout = 30;
      idle.setIdle(session_timeout) // how long can they be inactive before considered idle, in seconds
      idle.setTimeout(this.TIMEOUT); // how long can they be idle before considered timed out, in seconds
      idle.setInterrupts(DEFAULT_INTERRUPTSOURCES); // provide sources that will "interrupt" aka provide events indicating the user is active

      console.log("Setting idle timeout to " + session_timeout + " seconds")
       
      // Some code snipped for brevity

      // do something when the user has timed out
      idle.onTimeout.subscribe(() => {
        console.log("onTimeout called")
        this.idleState = "TIMED_OUT"
        this.authSvc.logout()
        this.reset()
        this.router.navigate(['login'], { queryParams: { reason: "Session Timeout. Please re-login" } });
      });
      // do something as the timeout countdown does its thing
      });
  

        console.log("Done with setting idle timeout")
        });
      }
    
    reset() 
    {
        // we'll call this method when we want to start/reset the idle process
        // reset any component state and be sure to call idle.watch()
        this.idle.watch();
        this.idleState = "NOT_IDLE";
        this.countdown = null;
        this.lastPing = null;
    }

Note, that the ng-idle package has 4 subscription methods - onIdleStart, onIdleEnd, onTimeoutWarning and onTimeout. I have purposefully omitted the first three from the code above, so as to be brief in my writeup - otherwise they exist in the code. The this.sessionDataSvc.getSessionProperties() is working as the console.log message is showing the log message

Setting idle timeout to 30 seconds

is showing in the console log (as is the other log message "Done with setting idle timeout" in the code above). But none of the ng-idle subscriptions are firing.

As I mentioned above, if I was to comment out the lines

this.sessionDataSvc.getSessionProperties().subscribe(( {data} ) => {
  let session_timeout = data.sessionProperties.session_idle_timeout

and enable the line:

// let session_timeout = 30;

instead (this is the hard-coded case), the @ng-idle subscriptions work as designed and documented. So why are they not inside a service subscription?

BoomDizzle
  • 188
  • 1
  • 1
  • 12

1 Answers1

0

It's a bad practice to subscribe to an observable within another one, that is called "callback hell", for this, you need to handle your observables with high order operators from RxJs such as switchMap, concatMap, mergeMap and exhaustMap depending on what you want to achieve.

If you are not familiar with these operators, you could read this short explanation I gave a while back, or read the official docs

Given your code example, you could refactor your logic appropriately like the following, in this case, I am using switchMap, but, again, it depends on the requirements:

...
someFunction(): void {
 this.sessionDataSvc.getSessionProperties().pipe(
   switchMap(({ data }) => {
     // it should be const rather than let, you are not modifying its value
     const sessionTimeout = data.sessionProperties.session_idle_timeout;
  
     idle.setIdle(sessionTimeout);
     idle.setTimeout(this.TIMEOUT);
     idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
     return idle.onTimeout; // returns an Observable
   })
 )
 .subscribe(() => {
   this.idleState = "TIMED_OUT"
   this.authSvc.logout()
   this.reset()
   this.router.navigate(['login'], { queryParams: { reason: "Session Timeout. Please re-login" } });
 });
}

reset(): void {
 this.idle.watch();
 this.idleState = "NOT_IDLE";
 this.countdown = null;
 this.lastPing = null;
}
Andres2142
  • 2,622
  • 2
  • 14
  • 19
  • Thanks for the suggestions. Not very welll versed with rx, but will try to adopt what you wrote. But do u think that is the reason the inner subscriptions are not firing? – BoomDizzle Jul 27 '23 at 15:32