angularng-idle

how to pevent ng2-idle instantiating multiple times with each successful login


I have a problem where I set the idle to display a popup with a minute before session expiration. Then the session times out or the user logs out. The next time the user logs on, they now have 2 popups when there is a timeout. The user logs out and in again and now the have 3 popups, and so on. How do I destroy the current instance of Idle when the user logs out?

My setup is as follows:

constructor(private idle: Idle, ...) {}

ngOnInit() {
    this.setIdle();
}

private setIdle() {
    // Client activity timeout. 29 minutes to 'idle', 1 minute beyond that to timeout
    this.ngZone.runOutsideAngular(() => {
        // this.idle.setIdle(29 * 60);
        // this.idle.setTimeout(1 * 60);
        this.idle.setIdle(10);
        this.idle.setTimeout(10);
        this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);
    });


    this.idle.onTimeout.subscribe(() => {
        this.ngZone.run(() => {
            this.errorDialogRef.close();
            sessionStorage.setItem('sessionExpired', 'true');
            this.displayErrorModal(true);
        });

    });

    this.idle.onIdleStart.subscribe(() => {
        this.ngZone.run(() => {
            this.displayIdleWarning();
        });
    });

    this.ngZone.runOutsideAngular(() => {
        this.idle.watch();
    });
}

During the logout process, I tried this.idle.stop() and delete this.idle. But neither worked.

FYI: It has to run outside the angular zone or else our protractor tests break.

Update: Until I found the solution of zeroing out the arrays, I did try:

this.idle.onTimeout.unsubscribe();
this.idle.onIdleStart.unsubscribe();
this.idle.onIdleEnd.unsubscribe();

But that only resulted in the following error during the subsequent login:

core.es5.js?0445:1084 ERROR Error: Uncaught (in promise): ObjectUnsubscribedError: object unsubscribed 

It seems to me my component is holding on to its references or is not getting destroyed during logout.


Solution

  • I think the correct way of being able to unsubscribe to the observers is to subscribe using a Subscription variable and not by setting the observers array length to 0. The code in your question was almost correct. What you tried was:

    this.idle.onTimeout.unsubscribe();
    

    That would end the subscription that this.idle.onTimeout has on the observable that this.idle.onTimeout is subscribed to (that's why you get the ObjectUnsubscribedError error).

    What would have worked is something like this:

    private idleOnTimeout$: Subscription;
    
    // Subscribe in for example ngOnInit()
    this.idleOnTimeout$ = this.idle.onTimeout.subscribe();
    
    // Unsubscribe on ngOnDestroy()
    this.idleOnTimeout$.unsubscribe();
    

    The example code of ng-idle does not cover this as in their example ng-idle lives in the app's main component which will never get destroyed unless you completely reload the page (which also resets ng-idle).