Infinite Scrolling
This lesson preview is part of the Mastering RxJS: A Compact Journey from Beginner to Pro course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Mastering RxJS: A Compact Journey from Beginner to Pro, plus 80+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:03] Hi and welcome back again. I'm so glad you've made it this far.
[00:04 - 00:12] Now that we've got our filtering working, let's imagine the following scenario. Someone from the business comes to us and tells us that our application is slow .
[00:13 - 00:18] We are querying the whole list of products from the back end. It's loading everything in memory.
[00:19 - 00:21] That's why the application is way too slow. So we need to optimize for it.
[00:22 - 00:33] What we are going to do is build a pagination functionality. The first thing that we want to do is change our product service. For simplicity sake, let's fetch 10 results at a time.
[00:34 - 00:44] What we need to do is modify this result in here. In case you've forgotten, this is our fake back end in here. So all this functionality should happen in the back end and not in the front end.
[00:45 - 00:51] But for simplicity sake, we're doing it in here. We want to pass a page number to our fetch products method.
[00:52 - 01:03] And at the end, we want to return a slice to the whole set. This will be our starting point and this will be our end point of the total amount of result that we will be returning.
[01:04 - 01:07] Let's format this. So for the product service, we are all set.
[01:08 - 01:13] Now let's go ahead and modify the app component. So in here, we need to pass the initial page number.
[01:14 - 01:22] Let's call it current page value and let's assign it in here, which will be the first page. And in here, we want to do it this way.
[01:23 - 01:26] Let's format. Now let's start the application and the first step is done.
[01:27 - 01:34] We are only getting the first 10 results. The next thing that we want to do is actually fetch the second, the third, the fourth page, and so forth.
[01:35 - 01:44] There are two ways we can do this. The first one is a boring way. We could implement page numbers down here with the next and previous buttons.
[01:45 - 01:50] But this is the old way, this is the boring way. We will do it reactively, just like Facebook or TikTok.
[01:51 - 02:00] When we scroll down, the next page will automatically be fetched from the back end. It just so happened that I'm a lazy developer and I don't want to reinvent the wheel.
[02:01 - 02:08] So for the next part, I will use a package. The package that I am going to use is ngx-infinite-scroll Let's go ahead and install it.
[02:09 - 02:16] Now that we've installed it, we need to update our app component. First things first, we want to import the infinite scroll directive.
[02:17 - 02:25] Now that we've imported, we can use it in our HTML template. In our app component.html, there are only two things that we want to do.
[02:26 - 02:32] The first one, we want to add the infinite scroll directive. And the second one to create a method that we will bind to the scrolled event.
[02:33 - 02:36] Let's call this method on scroll. Simple as that. Declared it.
[02:37 - 02:50] And in our app component, let's just move it a little bit to the bottom under handle search method. Right, so this is ngx-infinite-scroll package that we are using, and it has a couple of default that we want to quickly go through.
[02:51 - 03:05] The first default is the distance, which has a number of two, but basically it means that when we are at 80% of our scroll view, then it automatically triggers the scroll event. Another default is the throttle, which is 150 milliseconds.
[03:06 - 03:11] The event gets triggered after this many milliseconds. pass after the user stops scrolling.
[03:12 - 03:24] I'll leave the link of this package in the description under this video so you can look through all the other options that there are as well. The last thing remaining, we want in our app component to implement this on scroll method.
[03:25 - 03:39] For now, let's just reassign this products observable. Let's call our product service and fetch the products for the search term let's give it an empty string and for the page number, we need to somehow tell our product service that the scroll has happened.
[03:40 - 03:43] And we need to fetch the next page. What we could do here is something like this.
[03:44 - 03:52] We could increment the current page value, pass this current page value to the fetch products function and then reassign it to the products. Let's see if it works.
[03:53 - 03:57] Now if we scroll down, the whole list gets reloaded. If I scroll again, the whole list gets reloaded again.
[03:58 - 04:13] The reason for this is that we are always reassigning the whole product list. What we need to do now is actually create a variable that will hold all our products in here. So the next time we fetch something, we actually want to append it to this list.
[04:14 - 04:22] But before we do that, there's an issue that we have in here and that is every time we scroll, we are going to reassign this observable over and over again. I want to address that one first.
[04:23 - 04:28] The onScroll method, it only needs to tell that we are requiring the next page . And that's about it.
[04:29 - 04:39] Doesn't have to do the fetching itself. What we actually need to do is create a subject that we can subscribe to and fetch the current page that the service needs to fetch from the back end.
[04:40 - 04:44] Let's go in here and create a behavior subject. Let's call it current page.
[04:45 - 04:52] The initial value should be one. By the way, there is no need to give it an explicit type, because TypeScript is smart enough to infer the type from the value itself.
[04:53 - 05:03] Now in our onScroll method, the only thing that we need to do is emit the next value and the next value is the current page value plus one. We don't need that anymore.
[05:04 - 05:07] And we definitely don't need this one. Let's format. All right.
[05:08 - 05:13] So whenever there is a new page number, we want to go to our service and fetch the data. Let's go ahead and implement it.
[05:14 - 05:19] We want to listen to the current page. When there is a new value, we want to go to the back end and fetch the next ten results.
[05:20 - 05:22] Let's subscribe to it. Let's name it page number.
[05:23 - 05:34] And in here, let's say the dot products observable, let's reassign it, product service. For search term let's give it an empty string. And the page number now is the current page number in here.
[05:35 - 05:50] We have the same problem as previously. We are still reassigning this. When I scroll down, I keep getting the list overwritten. Instead of overwriting it, one could think that this is easy to solve by just subscribing to this fetch product service.
[05:51 - 05:59] Then when we get the new product, just append new products list to the all product list. Now we want to display these all products in the UI.
[06:00 - 06:08] So we need to update in our app component.html, the data source. I will comment this out for now and I will just create a div container.
[06:09 - 06:17] And in here, I'll put the app product list and we will pass all products as input to the app product list. So this is complaining because it needs to be public.
[06:18 - 06:21] Let's fix that. Let's save and see if it works.
[06:22 - 06:27] Now in the browser, if we scroll down, nothing happens because we forgot to add the infinite scroll directive. Let's add that.
[06:28 - 06:32] Let's check it again. Now when we scroll down, we get the next batch of results.
[06:33 - 06:39] Then we get the new results as well. But what I see here is, I am expecting after 20 to have 21.
[06:40 - 06:47] So we have a bug in our code and the problem is in the on scroll method. The current page value is always set to one.
[06:48 - 06:53] So we actually never store the new page number in here. We are always fetching the second page, actually.
[06:54 - 06:55] Let's fix that. All right.
[06:56 - 07:10] Now, if we scroll, I am expecting to get the next batch of results and the next and the next as well and to make testing a little bit faster in our product service, I will change the delay to one second. Now let's do something similar for the filter term.
[07:11 - 07:21] I will remove the reassignment and in here I will subscribe and basically just copy paste it from here. So whenever we get the new product list, we will just append it to the existing product list.
[07:22 - 07:27] Let's format this and let's see if it works. Let's refresh the page and immediately we see the first problem.
[07:28 - 07:34] In our networking tab, we see that two lists are being fetched. When we scroll down, we can see that both lists are appended to each other.
[07:35 - 07:42] Now when we scroll down even further, we get the second list and we get the next list. Let's see if the search functionality works.
[07:43 - 07:46] Let me type in DSLR. Yes, I get a response.
[07:47 - 07:53] Looks like not everything is DSLR. We've done some short testing and we can immediately see that there is more than one issue.
[07:54 - 08:08] One of the issues is both observables operate on the same all products list, which quickly becomes out of sync. Technically, we could add some checks, some listeners, some extra logic to keep those two in sync so that they communicate with each other.
[08:09 - 08:20] Whenever there is a new search term, the list should probably reset to be an empty list, but it shouldn't be the case when it's just a scroll event. Also, we are not passing the filter term to the next page.
[08:21 - 08:27] So we don't actually know how the next page should be filtered. There are a lot of issues that are not as simple to solve this way.
[08:28 - 08:36] One could also think that we could use the operator decision tree. For example, we have multiple observables that we want to combine together as one observable.
[08:37 - 08:44] And let's say I want to output values from either of them. We could use the merge operator to merge those two observables into a single one.
[08:45 - 08:56] Let me show you quickly how it works. We have the merge operator and we have this filter observable and we want to merge it with the current page observable, then for simplicity, let's subscribe.
[08:57 - 09:03] And then we get a value and with that value, we have to do some logic in here. First of all, we need to make a distinction.
[09:04 - 09:10] Is this a filter value or is this a current page value? We maybe want to add some caching, something like this.
[09:11 - 09:19] Either way, this becomes quickly way too complicated than it can be. So this is the first issue with this current approach, keeping those two in sync together.
[09:20 - 09:25] The second issue is nested subscriptions. This is actually a big no-no in Angular, a big taboo, I would say.
[09:26 - 09:37] And the reason for this is nested subscriptions, besides not unsubscribing from those obviously, can lead to funny behavior, unpredictable behavior. Can lead to a lot of bugs and race conditions, which are not fun to deal with .
[09:38 - 09:42] In the next lesson, we will see how to prevent this kind of issues. We will see how to avoid nested subscriptions.
[09:43 - 09:52] And we will also see how to combine multiple observables together and keep those in sync. And lastly, how to fully utilize the power of the async pipe in the HTML template.