RxJS in Angular: Part III
In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

RxJS in Angular: Part III
In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.


In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.
In this new article we are going to go beyond that and try to solve problems without resorting to imperative approach first. Rather, we want to start thinking in a reactive way right away, visualizing our problem solving in terms of data flow and streams, rather than commands.
To do this, let's just start with a problem which is actually hard to crack using only imperative approach.
Implementing a "You have been inactive for a while" popup in RxJSLink to this section
If you use PayPal on your desktop browser, chances are you have seen this screen at least once:


Now obviously this feature can be very useful (and even important regarding end user security), so we must look for ways of implementing it the most explicit and simple way possible. So instead of trying to do it in an imperative way (using setTimeout
and whatnot) and then moving to RxJS, we will just implement it with RxJS from scratch to improve our reactive thinking.
First of all, we have to understand what the stream of our data will be. The final information we want to receive is a boolean value about if "has the user performed some sort of event like move the mouse or click somewhere in the past one minute or so?", so to get it we need to monitor and buffer those events for a while, using interval
:
<>Copyconst perMinute$ = interval(60_000);
After that we should decide the events we want to listen to, which we will consider as the user being somehow active, and merge
them:
<>Copyconst events$ = merge( fromEvent(document.body, 'click'), fromEvent(document.body, 'mousemove'), fromEvent(document.body, 'scroll'), );
This list of events is far from complete, but you can add as many events as you want to fit you needs.
After that we want to count how many events in a minute have the system recorded, and this is the most tricky part, but RxJS again provides us with a beautiful solution; an operator called bufferWhen
, which receives another Observable
to monitor events. What it does is start collecting events from the source stream, push them into an array, then, when the inner Observable
emits, emit that array and start over. In our case we will use our events$
Observable
as the source, and count the events using the interval timer (one minute):
<>Copyconst bufferedEvents$ = events$.pipe( bufferWhen(() => interval$), ).subscribe(console.log);
If we open the console now and interact with the page, we will something like this:


Here we see that every 5 seconds (I have reduced the interval size to 5 secs for the purpose of not having 10,000+ events in the console) our Observable
emits an array of all specified events in that past timeframe. So how can we know that the user was inactive? Well, if there have been no events in that time interval, the resulting Array
will be empty, thus our final code will look like this:
<>Copyconst bufferedEvents$ = events$.pipe( bufferWhen(() => interval$), filter(events => events.length === 0), // no events in a timeframe means an empty array ).subscribe(() => alert('You have been inactive for a minute!'));
RxJS can take a pretty complex problem and solve it in a declarative fashion in so few lines of code, that it can easily fit into a tweet!
Implementing a "You have been inactive for a while" notice popup in just a few lines of #rxjs pic.twitter.com/89woZghmj4
— Armen Vardanyan (@Armandotrue) September 22, 2020
Using RxJS to get dynamic information about the DOMLink to this section
Imagine a scenario where we want to create a "scroll to top" button on our page, but we want to show it only when the user has scrolled down a bit. We could write a HostBinding
, recalculate the distance fom top using window.scrollY
, compare it to a sufficient value (let's say the user has scrolled for 500px), store in a property as a boolean... but then again, we promised to use RxJS right away. So, let's turn on our reactive thinking: what is the information we need? It is whether the user has scrolled sufficiently, so window.scrollY
. When does this information change? When the user scrolls, of course. So, the source of our stream is the scroll event, and we should map it to the scrolled distance. Here we go:
<>Copy@Component({ selector: 'my-app', template: ` <div> lots of content that creates vertical scroll here <button *ngIf="showBtn$ | async">Scroll to top</button> </div> `, }) export class MyComponent { showBtn$ = fromEvent(document, 'scroll').pipe( map(() => window.scrollY > 500), ); }
So, now this is very concise, short, and works perfectly!
Well, almost. If we add a tap(() => console.log('Working'))
to our Observable
, we would see that the even is triggered just too many times. How we fix it? Well, to be completely honest we don't need to recalculate the distance all the time; it's fairly sufficient to debounce that process a little. See where I'm going with this? Yes, we can just add debounceTime(50)
to make it run after there has been a 50 milliseconds (yes, 50 seconds ) pause between scrolling. This will significantly reduce the amount of value changes (and re-renders in some cases) that will happen without taking away the smoothness of transition of button being visible and not being visible. While this is a minor optimization, it opens door to a huge discussion about how we can make our code in Angular more performant using RxJS, which we will dive into deeper in the next chapter.
Comments (0)
Be the first to leave a comment
About the author

Senior Angular developer from Armenia. Passionate about WebDev, Football, Chess and Music

About the author
Armen Vardanyan
Senior Angular developer from Armenia. Passionate about WebDev, Football, Chess and Music
About the author

Senior Angular developer from Armenia. Passionate about WebDev, Football, Chess and Music