Angular 6 ng-idle
Asked Answered
R

5

6

I have an Angular Project which is working well and I'm implementing NG-IDLE and KeepAlive in order to keep the session fresh and to log a user out before the API session expires.

My issue is that the ng-idle is also operating on the login page, which is obviously not required, as when it does time out, it will take the person to the login page.

So I have the ng-idle and KeepAlive up and running in my app.component.ts but since I'm using lazy loading, I also have an authentication.module.ts and a login.component.ts.

The code in my root app.component.ts is as follows:

import { Component } from '@angular/core';

import { Idle, DEFAULT_INTERRUPTSOURCES } from '@ng-idle/core';
import { Keepalive } from '@ng-idle/keepalive';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

    idleState = 'Not started.';
    timedOut = false;
    lastPing?: Date = null;

    constructor(private idle: Idle, private keepalive: Keepalive) {

        // sets an idle timeout of 5 seconds, for testing purposes.
        idle.setIdle(5);

        // sets a timeout period of 5 seconds. after 10 seconds of inactivity, the user will be considered timed out.
        idle.setTimeout(5);

        // sets the default interrupts, in this case, things like clicks, scrolls, touches to the document
        idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);

        idle.onIdleEnd.subscribe(() => this.idleState = 'No longer idle.');

        idle.onTimeout.subscribe(() => {
            this.idleState = 'Timed out!';
            this.timedOut = true;
        });

        idle.onIdleStart.subscribe(() => this.idleState = 'You\'ve gone idle!');
        idle.onTimeoutWarning.subscribe((countdown) => this.idleState = 'You will time out in ' + countdown + ' seconds!');

        // Sets the ping interval to 15 seconds
        keepalive.interval(15);

        keepalive.onPing.subscribe(() => this.lastPing = new Date());

        this.reset();
    }

    reset() {
        this.idle.watch();
        this.idleState = 'Started.';
        this.timedOut = false;
    }
}

I know I need to call idle.unwatch in order to prevent idle running and idle.watch when I need it to, but how can I either call these from the login or authentication module, or can I detect from the root app.component.ts?

As no doubt you can tell that I'm new to Angular, so apologies if this is a rookie question.

Randle answered 31/12, 2018 at 14:28 Comment(0)
R
6

Since there are always more than one way to skin a cat, here's my own solution to this issue. I hope someone else finds it useful in the future.

import { Component } from '@angular/core';

import { Location } from '@angular/common';
import { Router } from '@angular/router';

import { Idle, DEFAULT_INTERRUPTSOURCES } from '@ng-idle/core';
import { Keepalive } from '@ng-idle/keepalive';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {

    currentPath: String;

    idleState = 'Not started.';
    timedOut = false;
    lastPing?: Date = null;

    constructor(private idle: Idle, private keepalive: Keepalive, location: Location, router: Router) {

        // sets an idle timeout of 5 seconds, for testing purposes.
        idle.setIdle(5);

        // sets a timeout period of 5 seconds. after 10 seconds of inactivity, the user will be considered timed out.
        idle.setTimeout(5);

        // sets the default interrupts, in this case, things like clicks, scrolls, touches to the document
        idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);

        idle.onIdleEnd.subscribe(() => this.idleState = 'No longer idle.');

        idle.onTimeout.subscribe(() => {
            this.idleState = 'Timed out!';
            this.timedOut = true;
        });

        idle.onIdleStart.subscribe(() => this.idleState = 'You\'ve gone idle!');
        idle.onTimeoutWarning.subscribe((countdown) => this.idleState = 'You will time out in ' + countdown + ' seconds!');

        // Sets the ping interval to 15 seconds
        keepalive.interval(15);

        keepalive.onPing.subscribe(() => this.lastPing = new Date());

        // Lets check the path everytime the route changes, stop or start the idle check as appropriate.
        router.events.subscribe((val) => {

            this.currentPath = location.path();
            if(this.currentPath.search(/authentication\/login/gi) == -1)
                idle.watch();
            else
                idle.stop();

        });
    }

    reset() {
        this.idle.watch();
        this.idleState = 'Started.';
        this.timedOut = false;
    }
}
Randle answered 31/12, 2018 at 15:40 Comment(2)
Yes I know the reset() function is no longer required in this case.Randle
Great code, for more clarity, follows this link for install dependencies. hackedbychinese.github.io/ng2-idle and then use [link] (stackoverflow.com/users/436315/jim-grant) for multi tab idle. Great work @Jim GrantParonomasia
P
1

You shouldn't be using such an ancient package as ng-idle today, when implementing inactivity tracking is so simple. Here's an example that I use in my projects:

import {Injectable} from '@angular/core';
import { filter, from, fromEvent, mergeAll, Observable, of, repeat, timeout} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class InactivityService {

  readonly timeoutDelay = 1000 * 60 * 5; // 5 minutes
  readonly $onInactive: Observable<void>;

  constructor() {
    const events = ['keypress', 'click', 'wheel', 'mousemove', 'ontouchstart'];
    this.$onInactive = from(events.map(e => fromEvent(document, e)))
      .pipe(
        mergeAll(),
        timeout({each: this.timeoutDelay, with: () => of(undefined as any)}),
        filter(a => !a),
        repeat()
      );
  }
}

Then inside your app's component:

constructor(private idle: InactivityService) {

  this.idle.$onInactive.subscribe(a => {
    if (/* is user logged in */) {
      // then log out and redirect to the Login page;
    }
  });
}
Prefecture answered 21/4, 2023 at 18:47 Comment(0)
F
0

One way is to have a home for routes other than login. All watch and un-watch logic can be moved here from app.component.ts

In app.routing.ts

const routes: Routes = [
    { path: 'login', component: LoginComponent },
    {
        path: '', component: HomeComponent,
        children: [
            // add auth requiring routes here
        ]
    }
];

In home.component.ts

export class HomeComponent implements OnDestroy {
    constructor() {
        // start watching here
    }  

    ngOnDestroy() {
        // unwatch here
    }
}

In home.component.html

<router-outlet></router-outlet>
Fasano answered 31/12, 2018 at 15:30 Comment(1)
A good idea, but I also have multiple routing.ts files. I've gone about it differently, see below.Randle
L
0

Great answer from user2191410, when you got separated all routes, is so simple for stoping. Thanks a lot, this was a guide for me.

I have this:

export class InitComponent implements OnInit {

  start watch()

}

ngOnDestroy() {
    // unwatch here
    this.idle.stop();
  }

In my routing:

{ path: 'dashboard',
      component: InitComponent,
      canActivate: [ AuthGuard ],
      children: [
          { path: '', component: DashboardComponent }
          All my pages are here...
           ]

}

The login component is out from InitComponent, like this:

const routes: Routes = [
    
  { path: 'login', component: LoginComponent },
  { path: 'lock', component: LockComponent },
  { path: 'recover', component: RecoverComponent }

];

AppComponent have the first router-outlet

<router-outlet></router-outlet>    

And finally my InitComponents have the second router-outlet

<router-outlet></router-outlet>
      
Lucrecialucretia answered 18/9, 2020 at 19:29 Comment(1)
Is this an extremely verbose "Thanks" or is there an actual answer to the question hidden here?Delius
P
0

You can check if the user is logged in first before you start the ng-idle

  this.currentUser = this.authenticationService.currentUserValue;

  if (this.currentUser) {
    this.ifIsLogged = true ;

    // sets an idle timeout of 5 seconds, for testing purposes.
    idle.setIdle(3000000);
    // sets a timeout period of 5 seconds. after 20 seconds of inactivity, the user will be considered timed out.
    idle.setTimeout(2000000);
    // sets the default interrupts, in this case, things like clicks, scrolls, touches to the document
    idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);

    idle.onIdleEnd.subscribe(() => {
      this.idleState = 'No longer idle.' ;
      alert('No longer idle. ') ;
    });

    idle.onTimeout.subscribe(() => {
      this.idleState = 'Timed out!';
      this.timedOut = true;
      this.logout() ;
    });
    
    idle.onIdleStart.subscribe(() => {
      this.idleState = 'You\'ve gone idle!' ;
    });

    idle.onTimeoutWarning.subscribe((countdown) => {
      this.idleState = 'You will time out in ' + countdown + ' seconds!' ;
    });

    // sets the ping interval to 15 seconds
    keepalive.interval(15);
    keepalive.onPing.subscribe(() => this.lastPing = new Date());
    this.reset();

  }
Proteiform answered 26/9, 2020 at 16:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.