Sometimes it's important to know whether a user has been inactive for a while. That's especially true when displaying sensitive information on the screen, which should be hidden if a longer period of inactivity is detected.
This post will guide you through implementing user inactivity detection in Angular. No time to waste!
Let's start with a simple service:
There's nothing groundbreaking here. Using the fromEvent function from RxJS we subscribe to events indicating that the user is still there (keydown, click, wheel, and mousemove). When any of the events is fired, the current date is saved to local storage and published to the lastActive BehaviorSubject that is available to other services via the lastActive public Observable.
Now we only need to figure out how to call the setUp method when the application starts. Thankfully, Angular provides us with the option to do just that using the APP_INITIALIZER DI token.
To make use of it, add the following to your AppModule's NgModuleproviders section:
And that's it - to check if everything is working as intended, let's create a component that will display how long ago the user was active.
Then, add the component onto a page and test it out.
It works 🥳
Note: the amTimeAgo pipe comes from the ngx-moment package.
Looks good! There's one caveat, though. Since we subscribed to the mousemove event on the whole document, the LastActiveService class updates the local storage entry and publishes new values to the lastActive$ Observable a lot. That might lead to performance issues down the line due to many unnecessary re-renders of dependent components.
Now, there are at least two ways to limit the number of updates broadcasted from the service. The first one involves a simple modification to the recordLastActiveDate method that forces an early return when too little time has passed since the last update.
This works but is certainly not the most elegant solution. The second one achieves the same result but is powered by RxJS magic.
To me, this is much cleaner, but if you prefer the first version, go for it!
There is one more thing that could be improved here. If you open your application in two separate tabs, one of them will have outdated information about the user's last activity date.
To overcome this, we can use the storage event to "communicate" between different tabs. Update the setUp method with the following code:
That should do it 🎉.
So far we only used the (in)activity detection to display when the user was last active. Let's do one more example. Image a service:
And a component:
These allow the user to "log in". What about logging out? Let's log out the user automatically if they are inactive for 10 seconds. First, let's add a method to LastActiveService that will return the user's last active date.
Then, we can use that in the LoginService:
The setUp method must be hooked up similarly to the one in the LastActiveService:
Disclaimer: Some people might not like me using moment.js instead of a more modern library. The main reason for that is the ngx-moment module which provides the amTimeAgo pipe. I wanted to keep things simple and avoid writing a pipe in this post. I checked a few other packages for libraries such as Luxon and date-fns, but they simply did not work as well as ngx-moment.