Use Angular and Firestore to add infinite scroll pagination to your app from scratch - both downward and upward. https://angularfirebase.com/lessons/infinite-scroll-firestore-angular/

Firestore: https://cloud.google.com/firestore/docs/
Angular Directives: https://angular.io/guide/attribute-directives
2024-02-04T07:59:03.930Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
cadad105-aa49-42c0-b95a-34244f5121f3
c2733069d3e3dde8571a0d6192050477ab07d0048bfd4cc424e878155be12403
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
Infinite Scroll is a feature that can have a huge impact on the user experience by loading data asynchronously based on scroll position.
In In this video, I'm going to show you how to implement infinite scroll in Angular with the Firestore database, but we're going to do it in a way that not only allows you to scroll down to load content, but also to scroll up and load content in the opposite direction.
This has been a commonly requested feature and it would be used if you were doing something like Facebook Messenger or Slack where you load a timeline of messages in reverse.
We're going to accomplish this by creating a generic service in Angular and it will not only retrieve the data, but also tell us when we've reached the end of a collection, and when we are loading new data.
If you're new to the channel, make sure to subscribe and grab the source code for this project from angularfirebase.com.
I'm going to be starting from a brand new Angular app and its only dependency is AngularFire, so make sure to go to the documentation and follow the setup instructions there.
Then the next step is to create a directive called scrollable.
This is going to tell us whether or not the user has scrolled to either the top or bottom of the page.
We're going to do that by monitoring the scroll event on the element itself.
That'd be the container element.
And then we're going to emit our own custom event that It will tell us whether or not it's the bottom or top.
You can create a custom event in Angular by using the Output decorator and Event emitter.
So our custom event here is named scroll position.
Then I'm going to add ElementRef to the constructor.
But I want to warn you that using ElementRef for direct DOM access can cause issues in Angular with platform server and web worker.
We can address this issue by wrapping native elements in a try catch Block, then provide a fallback such as a load more button when the DOM doesn't actually exist.
First, we'll set up a host listener on the element, Then listen to the scroll event, so keep in mind that your host element needs to be something scrollable.
Then we'll set up a function here and put our try catch block inside Then we need 3 values from the native element itself.
We need scroll top, scroll height, and offset height.
First, we need to know if the user has reached the bottom of the element.
So we can do that by saying top greater than height minus offset minus one extra pixel.
When that condition is met, we can have our event emitter send out a string value of bottom.
For top, we can simply see if the scroll top is at 0.
Before we go any further, let's make sure this works in an actual component.
So I'm going to switch over to the app component, then I'm going to import observable as well as the pagination service, which we're going to create in the next step.
Then I am setting up an event handler here that will just console log the event from our custom event in the directive.
All we need to do is set up a div and inside that div we'll add the scrollable directive.
And then for the event we use parenthesis c's with the scroll position event and then we'll run our scroll handler.
And I'm going to add an extra div in here with some height to it just so we can actually scroll through this parent div.
If you watch the console to the right, you'll see we either get a bottom or top event once we've reached the bottom or top of the element itself.
And before we get to Firestore, let's generate a component called loading spinner.
Then head over to SpinKit and choose your favorite spinner and just Copy and paste the HTML and CSS into that component.
Before we build this big complex pagination service, I wanna show you how it's going to be used in the component.
To paginate in Firestore, you need to send a new query for each new batch of data that you need.
The service I'm creating will allow you to make a Query with some basic configuration options, and this will populate an observable on the service that we can subscribe to.
The first argument to init is the collection and the second is the field that you wanna order by.
You can order the collection in reverse And you can also prepend new items to the beginning which is what you would use if you were doing an upward infinite scroll.
After the initial query, we'll listen for events in our scroll handler.
Once we receive the bond event, we can just call the more method from the service and that will populate the next batch of items from Firestore.
So the service we're building will give you infinite scroll with just two lines of code.
The service will also give us a data variable that's an observable array that we can loop over with the async pipe.
So we'll display some information about our boats in the database, but the service also has 2 other variables that are important.
The first one will tell us once we've reached the end of a collection and that's the done variable.
So we can listen for that and then say I ran out of boats once we reach the end.
We have another observable that will tell us whether or not a new query is loading so we can show a loading spinner for each new query, and that also helps us prevent sending multiple unnecessary queries.
If we take a look in Firestore quickly, we just have a boats collection that's in the root, and each document has a URL and a year field.
So we'll be sorting by the year.
When we look in the app we get the initial loading spinner and then once we scroll down the loading spinner will reappear And about a second later, we'll get some new data loaded.
Now we're finally ready to start building the service.
We're going to need Angular Firestore And we're also going to make pretty heavy usage of RxJS behavior subjects.
Next, I'm going to set up an interface that defines the query configuration options.
So we have a required field of path which is the path to your collection and then we have the field that you wanna order by.
Then we'll set default values for limit, reverse, and Penned.
Prepend's the important one.
That's the one that you would use if you wanted to have the items at the beginning of the array instead of added to the end of the array.
Next, I'm going to set up a few behavior subjects.
The subjects are all private variables and they have underscores so you can keep track of them easily in the code.
Done will tell us once we've reached the end of the collection.
Loading will tell us when a query is in process, And data is the last batch of information that has been retrieved from Firebase.
Then we'll set the query configuration as a variable so we can keep track of it between queries.
And that'll be typed to our Query config interface.
Then for each of these subjects, we're going to set an observable that will be consumed by the component.
This will keep all of the data changes isolated to the service.
This part's technically optional, but it is considered a good practice.
All we need to do is call as observable on the source data.
Then the next step is to add AngularFirestore to the constructor.
Then we're gonna go through a lot of code here, so I recommend checking out the main article for detailed explanations on each of these methods.
The first one we'll set up is init, which is going to make an initial query as well as save our query configuration on the service.
It requires a path to the collection as well as a field to order by and then it has optional defaults here that you can override by passing them as an object.
So we'll set a default limit of 2 and then say reverse false and prepend false.
Now we can use this object to make a reference to the Firestore collection.
We're going to order by the query field.
If the reverse property is set to true, then we'll go ahead and order it descending.
Otherwise, it'll be ascending.
Then we'll go ahead and limit it by whatever is set in the query configuration.
Then I have a map and update method that I'm going to show you in just a second here.
But first, I'm gonna show you how we and the observable data that will actually be used in the front end component.
So we'll take our source data, and then we'll call the RxJS scan operator on it.
Scan allows us to build a larger array over time.
If the query options are set to prepend, then we wanna take the new value and concatenate it to the beginning of the array.
And if it's not set, then we just wanna go ahead and concatenate it to the end.
Scan is a pretty weird operator to learn, so I recommend going to RxJS Marbles and checking it out visually.
Now let's take a look at that map and update method.
It takes the AngularFirestore collection and then maps it down to the snapshot that we need and then Sends it to our main data source.
If the collection is already done or loading, we don't wanna make the query so we'll just return out of the function.
But, otherwise, we'll set the loading state to true, and then we'll call collection snapshot changes.
To paginate Cursor in Firestore, you have to use the document snapshot.
So what we're going to do is map down the array to the data that we wanna show on the front end as well as the snapshot.
You'll see in a second here how we use the snapshot to offset all future queries.
Then after we have this data mapped down, we're going to check and see if the prepend option is set.
Then we need to reverse Each individual batch to maintain a consistent order going in reverse.
There may be a better way to do this, but I wasn't able to figure one out for the upward Scroll when you're prepending new items to a list.
After we have the values mapped down, we'll go ahead and update them in our main data source.
At this point, we can set our loading state to false and we can also check to see if that was an empty array, at which point we know we have reached the end of the collection.
And this is going to be a one off operation for each new query, so we're going to say take 1 and subscribe.
This will end your real time connection with Firebase, but pagination with real time data introduces a whole other host of issues and trade offs that you have to think about.
It's beyond the scope of this video, but please reach out on Slack if you wanna talk about it.
Now we're ready to set up a method to make secondary queries.
So first we need to know the cursor that we wanna paginate from.
If we're prepending, we want the document snapshot from the first item in the last batch.
Otherwise, we want the last item in the last batch.
If the array is empty, we want to return null.
Otherwise, we will get an error from Firestore.
The more method is going to make all subsequent queries.
First, we need to get the cursor for the pagination, and then we'll create a Firestore collection reference.
It's identical to the initial query except that it uses start after and passing that cursor as an argument.
This tells Firestore where to offset the query from.
Then once we have that set, we can call map an update, and our data source will be updated.
That was a lot of code to get through in 10 minutes, but make sure to check out the full article for detailed explanations on all these methods.
My goal is to give you a generic service that can work with any Firestore collection and give you infinite scroll either upwards or downwards.
That's it for Firestore Infinite Scroll with Angular.
If this video helped you, please like and subscribe.
And if you want twice as much content every week, Consider becoming a pro member at angularfirebase.com.
You'll get exclusive content, a free copy of my book, and 1 on 1 project consulting.
Thanks for watching, and I'll see you next time.

Speaker 0: Infinite Scroll is a feature that can have a huge impact on the user experience by loading data asynchronously based on scroll position. In In this video, I'm going to show you how to implement infinite scroll in Angular with the Firestore database, but we're going to do it in a way that not only allows you to scroll down to load content, but also to scroll up and load content in the opposite direction. This has been a commonly requested feature and it would be used if you were doing something like Facebook Messenger or Slack where you load a timeline of messages in reverse. We're going to accomplish this by creating a generic service in Angular and it will not only retrieve the data, but also tell us when we've reached the end of a collection, and when we are loading new data. If you're new to the channel, make sure to subscribe and grab the source code for this project from angularfirebase.com.

I'm going to be starting from a brand new Angular app and its only dependency is AngularFire, so make sure to go to the documentation and follow the setup instructions there. Then the next step is to create a directive called scrollable. This is going to tell us whether or not the user has scrolled to either the top or bottom of the page. We're going to do that by monitoring the scroll event on the element itself. That'd be the container element.

And then we're going to emit our own custom event that It will tell us whether or not it's the bottom or top. You can create a custom event in Angular by using the Output decorator and Event emitter. So our custom event here is named scroll position. Then I'm going to add ElementRef to the constructor. But I want to warn you that using ElementRef for direct DOM access can cause issues in Angular with platform server and web worker.

We can address this issue by wrapping native elements in a try catch Block, then provide a fallback such as a load more button when the DOM doesn't actually exist. First, we'll set up a host listener on the element, Then listen to the scroll event, so keep in mind that your host element needs to be something scrollable. Then we'll set up a function here and put our try catch block inside Then we need 3 values from the native element itself. We need scroll top, scroll height, and offset height. First, we need to know if the user has reached the bottom of the element.

So we can do that by saying top greater than height minus offset minus one extra pixel. When that condition is met, we can have our event emitter send out a string value of bottom. For top, we can simply see if the scroll top is at 0. Before we go any further, let's make sure this works in an actual component. So I'm going to switch over to the app component, then I'm going to import observable as well as the pagination service, which we're going to create in the next step.

Then I am setting up an event handler here that will just console log the event from our custom event in the directive. All we need to do is set up a div and inside that div we'll add the scrollable directive. And then for the event we use parenthesis c's with the scroll position event and then we'll run our scroll handler. And I'm going to add an extra div in here with some height to it just so we can actually scroll through this parent div. If you watch the console to the right, you'll see we either get a bottom or top event once we've reached the bottom or top of the element itself.

And before we get to Firestore, let's generate a component called loading spinner. Then head over to SpinKit and choose your favorite spinner and just Copy and paste the HTML and CSS into that component. Before we build this big complex pagination service, I wanna show you how it's going to be used in the component. To paginate in Firestore, you need to send a new query for each new batch of data that you need. The service I'm creating will allow you to make a Query with some basic configuration options, and this will populate an observable on the service that we can subscribe to.

The first argument to init is the collection and the second is the field that you wanna order by. You can order the collection in reverse And you can also prepend new items to the beginning which is what you would use if you were doing an upward infinite scroll. After the initial query, we'll listen for events in our scroll handler. Once we receive the bond event, we can just call the more method from the service and that will populate the next batch of items from Firestore. So the service we're building will give you infinite scroll with just two lines of code.

The service will also give us a data variable that's an observable array that we can loop over with the async pipe. So we'll display some information about our boats in the database, but the service also has 2 other variables that are important. The first one will tell us once we've reached the end of a collection and that's the done variable. So we can listen for that and then say I ran out of boats once we reach the end. We have another observable that will tell us whether or not a new query is loading so we can show a loading spinner for each new query, and that also helps us prevent sending multiple unnecessary queries.

If we take a look in Firestore quickly, we just have a boats collection that's in the root, and each document has a URL and a year field. So we'll be sorting by the year. When we look in the app we get the initial loading spinner and then once we scroll down the loading spinner will reappear And about a second later, we'll get some new data loaded. Now we're finally ready to start building the service. We're going to need Angular Firestore And we're also going to make pretty heavy usage of RxJS behavior subjects.

Next, I'm going to set up an interface that defines the query configuration options. So we have a required field of path which is the path to your collection and then we have the field that you wanna order by. Then we'll set default values for limit, reverse, and Penned. Prepend's the important one. That's the one that you would use if you wanted to have the items at the beginning of the array instead of added to the end of the array.

Next, I'm going to set up a few behavior subjects. The subjects are all private variables and they have underscores so you can keep track of them easily in the code. Done will tell us once we've reached the end of the collection. Loading will tell us when a query is in process, And data is the last batch of information that has been retrieved from Firebase. Then we'll set the query configuration as a variable so we can keep track of it between queries.

And that'll be typed to our Query config interface. Then for each of these subjects, we're going to set an observable that will be consumed by the component. This will keep all of the data changes isolated to the service. This part's technically optional, but it is considered a good practice. All we need to do is call as observable on the source data.

Then the next step is to add AngularFirestore to the constructor. Then we're gonna go through a lot of code here, so I recommend checking out the main article for detailed explanations on each of these methods. The first one we'll set up is init, which is going to make an initial query as well as save our query configuration on the service. It requires a path to the collection as well as a field to order by and then it has optional defaults here that you can override by passing them as an object. So we'll set a default limit of 2 and then say reverse false and prepend false.

Now we can use this object to make a reference to the Firestore collection. We're going to order by the query field. If the reverse property is set to true, then we'll go ahead and order it descending. Otherwise, it'll be ascending. Then we'll go ahead and limit it by whatever is set in the query configuration.

Then I have a map and update method that I'm going to show you in just a second here. But first, I'm gonna show you how we and the observable data that will actually be used in the front end component. So we'll take our source data, and then we'll call the RxJS scan operator on it. Scan allows us to build a larger array over time. If the query options are set to prepend, then we wanna take the new value and concatenate it to the beginning of the array.

And if it's not set, then we just wanna go ahead and concatenate it to the end. Scan is a pretty weird operator to learn, so I recommend going to RxJS Marbles and checking it out visually. Now let's take a look at that map and update method. It takes the AngularFirestore collection and then maps it down to the snapshot that we need and then Sends it to our main data source. If the collection is already done or loading, we don't wanna make the query so we'll just return out of the function.

But, otherwise, we'll set the loading state to true, and then we'll call collection snapshot changes. To paginate Cursor in Firestore, you have to use the document snapshot. So what we're going to do is map down the array to the data that we wanna show on the front end as well as the snapshot. You'll see in a second here how we use the snapshot to offset all future queries. Then after we have this data mapped down, we're going to check and see if the prepend option is set.

Then we need to reverse Each individual batch to maintain a consistent order going in reverse. There may be a better way to do this, but I wasn't able to figure one out for the upward Scroll when you're prepending new items to a list. After we have the values mapped down, we'll go ahead and update them in our main data source. At this point, we can set our loading state to false and we can also check to see if that was an empty array, at which point we know we have reached the end of the collection. And this is going to be a one off operation for each new query, so we're going to say take 1 and subscribe.

This will end your real time connection with Firebase, but pagination with real time data introduces a whole other host of issues and trade offs that you have to think about. It's beyond the scope of this video, but please reach out on Slack if you wanna talk about it. Now we're ready to set up a method to make secondary queries. So first we need to know the cursor that we wanna paginate from. If we're prepending, we want the document snapshot from the first item in the last batch.

Otherwise, we want the last item in the last batch. If the array is empty, we want to return null. Otherwise, we will get an error from Firestore. The more method is going to make all subsequent queries. First, we need to get the cursor for the pagination, and then we'll create a Firestore collection reference.

It's identical to the initial query except that it uses start after and passing that cursor as an argument. This tells Firestore where to offset the query from. Then once we have that set, we can call map an update, and our data source will be updated. That was a lot of code to get through in 10 minutes, but make sure to check out the full article for detailed explanations on all these methods. My goal is to give you a generic service that can work with any Firestore collection and give you infinite scroll either upwards or downwards.

That's it for Firestore Infinite Scroll with Angular. If this video helped you, please like and subscribe. And if you want twice as much content every week, Consider becoming a pro member at angularfirebase.com. You'll get exclusive content, a free copy of my book, and 1 on 1 project consulting. Thanks for watching, and I'll see you next time.
Infinite Scroll is a feature that can have a huge impact on the user experience by loading data asynchronously based on scroll position. In In this video, I'm going to show you how to implement infinite scroll in Angular with the Firestore database, but we're going to do it in a way that not only allows you to scroll down to load content, but also to scroll up and load content in the opposite direction. This has been a commonly requested feature and it would be used if you were doing something like Facebook Messenger or Slack where you load a timeline of messages in reverse. We're going to accomplish this by creating a generic service in Angular and it will not only retrieve the data, but also tell us when we've reached the end of a collection, and when we are loading new data. If you're new to the channel, make sure to subscribe and grab the source code for this project from angularfirebase.com. I'm going to be starting from a brand new Angular app and its only dependency is AngularFire, so make sure to go to the documentation and follow the setup instructions there. Then the next step is to create a directive called scrollable. This is going to tell us whether or not the user has scrolled to either the top or bottom of the page.
We're going to do that by monitoring the scroll event on the element itself. That'd be the container element. And then we're going to emit our own custom event that It will tell us whether or not it's the bottom or top. You can create a custom event in Angular by using the Output decorator and Event emitter.
animation
So our custom event here is named scroll position. Then I'm going to add ElementRef to the constructor. But I want to warn you that using ElementRef for direct DOM access can cause issues in Angular with platform server and web worker. We can address this issue by wrapping native elements in a try catch Block, then provide a fallback such as a load more button when the DOM doesn't actually exist. First, we'll set up a host listener on the element, Then listen to the scroll event, so keep in mind that your host element needs to be something scrollable. Then we'll set up a function here and put our try catch block inside Then we need 3 values from the native element itself. We need scroll top, scroll height, and offset height.
First, we need to know if the user has reached the bottom of the element. So we can do that by saying top greater than height minus offset minus one extra pixel. When that condition is met, we can have our event emitter send out a string value of bottom.
measurement
For top, we can simply see if the scroll top is at 0. Before we go any further, let's make sure this works in an actual component. So I'm going to switch over to the app component, then I'm going to import observable as well as the pagination service, which we're going to create in the next step. Then I am setting up an event handler here that will just console log the event from our custom event in the directive. All we need to do is set up a div and inside that div we'll add the scrollable directive. And then for the event we use parenthesis c's with the scroll position event and then we'll run our scroll handler. And I'm going to add an extra div in here with some height to it just so we can actually scroll through this parent div.
If you watch the console to the right, you'll see we either get a bottom or top event once we've reached the bottom or top of the element itself. And before we get to Firestore, let's generate a component called loading spinner. Then head over to SpinKit and choose your favorite spinner and just Copy and paste the HTML and CSS into that component.
animation
Before we build this big complex pagination service, I wanna show you how it's going to be used in the component. To paginate in Firestore, you need to send a new query for each new batch of data that you need. The service I'm creating will allow you to make a Query with some basic configuration options, and this will populate an observable on the service that we can subscribe to.
databases
The first argument to init is the collection and the second is the field that you wanna order by. You can order the collection in reverse And you can also prepend new items to the beginning which is what you would use if you were doing an upward infinite scroll. After the initial query, we'll listen for events in our scroll handler. Once we receive the bond event, we can just call the more method from the service and that will populate the next batch of items from Firestore.
So the service we're building will give you infinite scroll with just two lines of code. The service will also give us a data variable that's an observable array that we can loop over with the async pipe. So we'll display some information about our boats in the database, but the service also has 2 other variables that are important. The first one will tell us once we've reached the end of a collection and that's the done variable. So we can listen for that and then say I ran out of boats once we reach the end.
boats
data
databases
data management
fishing
We have another observable that will tell us whether or not a new query is loading so we can show a loading spinner for each new query, and that also helps us prevent sending multiple unnecessary queries. If we take a look in Firestore quickly, we just have a boats collection that's in the root, and each document has a URL and a year field. So we'll be sorting by the year. When we look in the app we get the initial loading spinner and then once we scroll down the loading spinner will reappear And about a second later, we'll get some new data loaded.
Now we're finally ready to start building the service. We're going to need Angular Firestore And we're also going to make pretty heavy usage of RxJS behavior subjects. Next, I'm going to set up an interface that defines the query configuration options. So we have a required field of path which is the path to your collection and then we have the field that you wanna order by. Then we'll set default values for limit, reverse, and Penned. Prepend's the important one. That's the one that you would use if you wanted to have the items at the beginning of the array instead of added to the end of the array. Next, I'm going to set up a few behavior subjects. The subjects are all private variables and they have underscores so you can keep track of them easily in the code. Done will tell us once we've reached the end of the collection. Loading will tell us when a query is in process, And data is the last batch of information that has been retrieved from Firebase. Then we'll set the query configuration as a variable so we can keep track of it between queries. And that'll be typed to our Query config interface. Then for each of these subjects, we're going to set an observable that will be consumed by the component. This will keep all of the data changes isolated to the service. This part's technically optional, but it is considered a good practice. All we need to do is call as observable on the source data. Then the next step is to add AngularFirestore to the constructor. Then we're gonna go through a lot of code here, so I recommend checking out the main article for detailed explanations on each of these methods.
data
data management
databases
programming
systems
documentation
The first one we'll set up is init, which is going to make an initial query as well as save our query configuration on the service. It requires a path to the collection as well as a field to order by and then it has optional defaults here that you can override by passing them as an object. So we'll set a default limit of 2 and then say reverse false and prepend false. Now we can use this object to make a reference to the Firestore collection. We're going to order by the query field. If the reverse property is set to true, then we'll go ahead and order it descending. Otherwise, it'll be ascending.
Then we'll go ahead and limit it by whatever is set in the query configuration. Then I have a map and update method that I'm going to show you in just a second here. But first, I'm gonna show you how we and the observable data that will actually be used in the front end component. So we'll take our source data, and then we'll call the RxJS scan operator on it. Scan allows us to build a larger array over time. If the query options are set to prepend, then we wanna take the new value and concatenate it to the beginning of the array. And if it's not set, then we just wanna go ahead and concatenate it to the end.
programming
data management
databases
data
Scan is a pretty weird operator to learn, so I recommend going to RxJS Marbles and checking it out visually.
Now let's take a look at that map and update method. It takes the AngularFirestore collection and then maps it down to the snapshot that we need and then Sends it to our main data source. If the collection is already done or loading, we don't wanna make the query so we'll just return out of the function.
databases
But, otherwise, we'll set the loading state to true, and then we'll call collection snapshot changes. To paginate Cursor in Firestore, you have to use the document snapshot. So what we're going to do is map down the array to the data that we wanna show on the front end as well as the snapshot. You'll see in a second here how we use the snapshot to offset all future queries. Then after we have this data mapped down, we're going to check and see if the prepend option is set. Then we need to reverse Each individual batch to maintain a consistent order going in reverse. There may be a better way to do this, but I wasn't able to figure one out for the upward Scroll when you're prepending new items to a list.
After we have the values mapped down, we'll go ahead and update them in our main data source. At this point, we can set our loading state to false and we can also check to see if that was an empty array, at which point we know we have reached the end of the collection.
data
data management
And this is going to be a one off operation for each new query, so we're going to say take 1 and subscribe. This will end your real time connection with Firebase, but pagination with real time data introduces a whole other host of issues and trade offs that you have to think about. It's beyond the scope of this video, but please reach out on Slack if you wanna talk about it. Now we're ready to set up a method to make secondary queries. So first we need to know the cursor that we wanna paginate from. If we're prepending, we want the document snapshot from the first item in the last batch. Otherwise, we want the last item in the last batch. If the array is empty, we want to return null. Otherwise, we will get an error from Firestore. The more method is going to make all subsequent queries. First, we need to get the cursor for the pagination, and then we'll create a Firestore collection reference. It's identical to the initial query except that it uses start after and passing that cursor as an argument. This tells Firestore where to offset the query from. Then once we have that set, we can call map an update, and our data source will be updated. That was a lot of code to get through in 10 minutes, but make sure to check out the full article for detailed explanations on all these methods. My goal is to give you a generic service that can work with any Firestore collection and give you infinite scroll either upwards or downwards.
That's it for Firestore Infinite Scroll with Angular. If this video helped you, please like and subscribe. And if you want twice as much content every week, Consider becoming a pro member at angularfirebase.com. You'll get exclusive content, a free copy of my book, and 1 on 1 project consulting. Thanks for watching, and I'll see you next time.
publishing
books
magazines
copyright
youtube
literature
Infinite Scroll is a feature that can have a huge impact on the user experience by loading data asynchronously based on scroll position. In In this video, I'm going to show you how to implement infinite scroll in Angular with the Firestore database, but we're going to do it in a way that not only allows you to scroll down to load content, but also to scroll up and load content in the opposite direction. This has been a commonly requested feature and it would be used if you were doing something like Facebook Messenger or Slack where you load a timeline of messages in reverse. We're going to accomplish this by creating a generic service in Angular and it will not only retrieve the data, but also tell us when we've reached the end of a collection, and when we are loading new data. If you're new to the channel, make sure to subscribe and grab the source code for this project from angularfirebase.com. I'm going to be starting from a brand new Angular app and its only dependency is AngularFire, so make sure to go to the documentation and follow the setup instructions there. Then the next step is to create a directive called scrollable. This is going to tell us whether or not the user has scrolled to either the top or bottom of the page. We're going to do that by monitoring the scroll event on the element itself. That'd be the container element. And then we're going to emit our own custom event that It will tell us whether or not it's the bottom or top. You can create a custom event in Angular by using the Output decorator and Event emitter. So our custom event here is named scroll position. Then I'm going to add ElementRef to the constructor. But I want to warn you that using ElementRef for direct DOM access can cause issues in Angular with platform server and web worker. We can address this issue by wrapping native elements in a try catch Block, then provide a fallback such as a load more button when the DOM doesn't actually exist. First, we'll set up a host listener on the element, Then listen to the scroll event, so keep in mind that your host element needs to be something scrollable. Then we'll set up a function here and put our try catch block inside Then we need 3 values from the native element itself. We need scroll top, scroll height, and offset height. First, we need to know if the user has reached the bottom of the element. So we can do that by saying top greater than height minus offset minus one extra pixel. When that condition is met, we can have our event emitter send out a string value of bottom. For top, we can simply see if the scroll top is at 0. Before we go any further, let's make sure this works in an actual component. So I'm going to switch over to the app component, then I'm going to import observable as well as the pagination service, which we're going to create in the next step. Then I am setting up an event handler here that will just console log the event from our custom event in the directive. All we need to do is set up a div and inside that div we'll add the scrollable directive. And then for the event we use parenthesis c's with the scroll position event and then we'll run our scroll handler. And I'm going to add an extra div in here with some height to it just so we can actually scroll through this parent div. If you watch the console to the right, you'll see we either get a bottom or top event once we've reached the bottom or top of the element itself. And before we get to Firestore, let's generate a component called loading spinner. Then head over to SpinKit and choose your favorite spinner and just Copy and paste the HTML and CSS into that component. Before we build this big complex pagination service, I wanna show you how it's going to be used in the component. To paginate in Firestore, you need to send a new query for each new batch of data that you need. The service I'm creating will allow you to make a Query with some basic configuration options, and this will populate an observable on the service that we can subscribe to. The first argument to init is the collection and the second is the field that you wanna order by. You can order the collection in reverse And you can also prepend new items to the beginning which is what you would use if you were doing an upward infinite scroll. After the initial query, we'll listen for events in our scroll handler. Once we receive the bond event, we can just call the more method from the service and that will populate the next batch of items from Firestore. So the service we're building will give you infinite scroll with just two lines of code. The service will also give us a data variable that's an observable array that we can loop over with the async pipe. So we'll display some information about our boats in the database, but the service also has 2 other variables that are important. The first one will tell us once we've reached the end of a collection and that's the done variable. So we can listen for that and then say I ran out of boats once we reach the end. We have another observable that will tell us whether or not a new query is loading so we can show a loading spinner for each new query, and that also helps us prevent sending multiple unnecessary queries. If we take a look in Firestore quickly, we just have a boats collection that's in the root, and each document has a URL and a year field. So we'll be sorting by the year. When we look in the app we get the initial loading spinner and then once we scroll down the loading spinner will reappear And about a second later, we'll get some new data loaded. Now we're finally ready to start building the service. We're going to need Angular Firestore And we're also going to make pretty heavy usage of RxJS behavior subjects. Next, I'm going to set up an interface that defines the query configuration options. So we have a required field of path which is the path to your collection and then we have the field that you wanna order by. Then we'll set default values for limit, reverse, and Penned. Prepend's the important one. That's the one that you would use if you wanted to have the items at the beginning of the array instead of added to the end of the array. Next, I'm going to set up a few behavior subjects. The subjects are all private variables and they have underscores so you can keep track of them easily in the code. Done will tell us once we've reached the end of the collection. Loading will tell us when a query is in process, And data is the last batch of information that has been retrieved from Firebase. Then we'll set the query configuration as a variable so we can keep track of it between queries. And that'll be typed to our Query config interface. Then for each of these subjects, we're going to set an observable that will be consumed by the component. This will keep all of the data changes isolated to the service. This part's technically optional, but it is considered a good practice. All we need to do is call as observable on the source data. Then the next step is to add AngularFirestore to the constructor. Then we're gonna go through a lot of code here, so I recommend checking out the main article for detailed explanations on each of these methods. The first one we'll set up is init, which is going to make an initial query as well as save our query configuration on the service. It requires a path to the collection as well as a field to order by and then it has optional defaults here that you can override by passing them as an object. So we'll set a default limit of 2 and then say reverse false and prepend false. Now we can use this object to make a reference to the Firestore collection. We're going to order by the query field. If the reverse property is set to true, then we'll go ahead and order it descending. Otherwise, it'll be ascending. Then we'll go ahead and limit it by whatever is set in the query configuration. Then I have a map and update method that I'm going to show you in just a second here. But first, I'm gonna show you how we and the observable data that will actually be used in the front end component. So we'll take our source data, and then we'll call the RxJS scan operator on it. Scan allows us to build a larger array over time. If the query options are set to prepend, then we wanna take the new value and concatenate it to the beginning of the array. And if it's not set, then we just wanna go ahead and concatenate it to the end. Scan is a pretty weird operator to learn, so I recommend going to RxJS Marbles and checking it out visually. Now let's take a look at that map and update method. It takes the AngularFirestore collection and then maps it down to the snapshot that we need and then Sends it to our main data source. If the collection is already done or loading, we don't wanna make the query so we'll just return out of the function. But, otherwise, we'll set the loading state to true, and then we'll call collection snapshot changes. To paginate Cursor in Firestore, you have to use the document snapshot. So what we're going to do is map down the array to the data that we wanna show on the front end as well as the snapshot. You'll see in a second here how we use the snapshot to offset all future queries. Then after we have this data mapped down, we're going to check and see if the prepend option is set. Then we need to reverse Each individual batch to maintain a consistent order going in reverse. There may be a better way to do this, but I wasn't able to figure one out for the upward Scroll when you're prepending new items to a list. After we have the values mapped down, we'll go ahead and update them in our main data source. At this point, we can set our loading state to false and we can also check to see if that was an empty array, at which point we know we have reached the end of the collection. And this is going to be a one off operation for each new query, so we're going to say take 1 and subscribe. This will end your real time connection with Firebase, but pagination with real time data introduces a whole other host of issues and trade offs that you have to think about. It's beyond the scope of this video, but please reach out on Slack if you wanna talk about it. Now we're ready to set up a method to make secondary queries. So first we need to know the cursor that we wanna paginate from. If we're prepending, we want the document snapshot from the first item in the last batch. Otherwise, we want the last item in the last batch. If the array is empty, we want to return null. Otherwise, we will get an error from Firestore. The more method is going to make all subsequent queries. First, we need to get the cursor for the pagination, and then we'll create a Firestore collection reference. It's identical to the initial query except that it uses start after and passing that cursor as an argument. This tells Firestore where to offset the query from. Then once we have that set, we can call map an update, and our data source will be updated. That was a lot of code to get through in 10 minutes, but make sure to check out the full article for detailed explanations on all these methods. My goal is to give you a generic service that can work with any Firestore collection and give you infinite scroll either upwards or downwards. That's it for Firestore Infinite Scroll with Angular. If this video helped you, please like and subscribe. And if you want twice as much content every week, Consider becoming a pro member at angularfirebase.com. You'll get exclusive content, a free copy of my book, and 1 on 1 project consulting. Thanks for watching, and I'll see you next time.
Infinite
infinite
Scroll
scroll
is
is
a
a
feature
feature
that
that
can
can
have
have
a
a
huge
huge
impact
impact
on
on
the
the
user
user
experience
experience
by
by
loading
loading
data
data
asynchronously
asynchronously
based
based
on
on
scroll
scroll
position.
position
In
in
In
in
this
this
video,
video
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
implement
implement
infinite
infinite
scroll
scroll
in
in
Angular
angular
with
with
the
the
Firestore
firestore
database,
database
but
but
we're
we're
going
going
to
to
do
do
it
it
in
in
a
a
way
way
that
that
not
not
only
only
allows
allows
you
you
to
to
scroll
scroll
down
down
to
to
load
load
content,
content
but
but
also
also
to
to
scroll
scroll
up
up
and
and
load
load
content
content
in
in
the
the
opposite
opposite
direction.
direction
This
this
has
has
been
been
a
a
commonly
commonly
requested
requested
feature
feature
and
and
it
it
would
would
be
be
used
used
if
if
you
you
were
were
doing
doing
something
something
like
like
Facebook
facebook
Messenger
messenger
or
or
Slack
slack
where
where
you
you
load
load
a
a
timeline
timeline
of
of
messages
messages
in
in
reverse.
reverse
We're
we're
going
going
to
to
accomplish
accomplish
this
this
by
by
creating
creating
a
a
generic
generic
service
service
in
in
Angular
angular
and
and
it
it
will
will
not
not
only
only
retrieve
retrieve
the
the
data,
data
but
but
also
also
tell
tell
us
us
when
when
we've
we've
reached
reached
the
the
end
end
of
of
a
a
collection,
collection
and
and
when
when
we
we
are
are
loading
loading
new
new
data.
data
If
if
you're
you're
new
new
to
to
the
the
channel,
channel
make
make
sure
sure
to
to
subscribe
subscribe
and
and
grab
grab
the
the
source
source
code
code
for
for
this
this
project
project
from
from
angularfirebase.com.
angularfirebase.com
I'm
i'm
going
going
to
to
be
be
starting
starting
from
from
a
a
brand
brand
new
new
Angular
angular
app
app
and
and
its
its
only
only
dependency
dependency
is
is
AngularFire,
angularfire
so
so
make
make
sure
sure
to
to
go
go
to
to
the
the
documentation
documentation
and
and
follow
follow
the
the
setup
setup
instructions
instructions
there.
there
Then
then
the
the
next
next
step
step
is
is
to
to
create
create
a
a
directive
directive
called
called
scrollable.
scrollable
This
this
is
is
going
going
to
to
tell
tell
us
us
whether
whether
or
or
not
not
the
the
user
user
has
has
scrolled
scrolled
to
to
either
either
the
the
top
top
or
or
bottom
bottom
of
of
the
the
page.
page
We're
we're
going
going
to
to
do
do
that
that
by
by
monitoring
monitoring
the
the
scroll
scroll
event
event
on
on
the
the
element
element
itself.
itself
That'd
that'd
be
be
the
the
container
container
element.
element
And
and
then
then
we're
we're
going
going
to
to
emit
emit
our
our
own
own
custom
custom
event
event
that
that
It
it
will
will
tell
tell
us
us
whether
whether
or
or
not
not
it's
it's
the
the
bottom
bottom
or
or
top.
top
You
you
can
can
create
create
a
a
custom
custom
event
event
in
in
Angular
angular
by
by
using
using
the
the
Output
output
decorator
decorator
and
and
Event
event
emitter.
emitter
So
so
our
our
custom
custom
event
event
here
here
is
is
named
named
scroll
scroll
position.
position
Then
then
I'm
i'm
going
going
to
to
add
add
ElementRef
elementref
to
to
the
the
constructor.
constructor
But
but
I
i
want
want
to
to
warn
warn
you
you
that
that
using
using
ElementRef
elementref
for
for
direct
direct
DOM
dom
access
access
can
can
cause
cause
issues
issues
in
in
Angular
angular
with
with
platform
platform
server
server
and
and
web
web
worker.
worker
We
we
can
can
address
address
this
this
issue
issue
by
by
wrapping
wrapping
native
native
elements
elements
in
in
a
a
try
try
catch
catch
Block,
block
then
then
provide
provide
a
a
fallback
fallback
such
such
as
as
a
a
load
load
more
more
button
button
when
when
the
the
DOM
dom
doesn't
doesn't
actually
actually
exist.
exist
First,
first
we'll
we'll
set
set
up
up
a
a
host
host
listener
listener
on
on
the
the
element,
element
Then
then
listen
listen
to
to
the
the
scroll
scroll
event,
event
so
so
keep
keep
in
in
mind
mind
that
that
your
your
host
host
element
element
needs
needs
to
to
be
be
something
something
scrollable.
scrollable
Then
then
we'll
we'll
set
set
up
up
a
a
function
function
here
here
and
and
put
put
our
our
try
try
catch
catch
block
block
inside
inside
Then
then
we
we
need
need
3
3
values
values
from
from
the
the
native
native
element
element
itself.
itself
We
we
need
need
scroll
scroll
top,
top
scroll
scroll
height,
height
and
and
offset
offset
height.
height
First,
first
we
we
need
need
to
to
know
know
if
if
the
the
user
user
has
has
reached
reached
the
the
bottom
bottom
of
of
the
the
element.
element
So
so
we
we
can
can
do
do
that
that
by
by
saying
saying
top
top
greater
greater
than
than
height
height
minus
minus
offset
offset
minus
minus
one
one
extra
extra
pixel.
pixel
When
when
that
that
condition
condition
is
is
met,
met
we
we
can
can
have
have
our
our
event
event
emitter
emitter
send
send
out
out
a
a
string
string
value
value
of
of
bottom.
bottom
For
for
top,
top
we
we
can
can
simply
simply
see
see
if
if
the
the
scroll
scroll
top
top
is
is
at
at
0.
0
Before
before
we
we
go
go
any
any
further,
further
let's
let's
make
make
sure
sure
this
this
works
works
in
in
an
an
actual
actual
component.
component
So
so
I'm
i'm
going
going
to
to
switch
switch
over
over
to
to
the
the
app
app
component,
component
then
then
I'm
i'm
going
going
to
to
import
import
observable
observable
as
as
well
well
as
as
the
the
pagination
pagination
service,
service
which
which
we're
we're
going
going
to
to
create
create
in
in
the
the
next
next
step.
step
Then
then
I
i
am
am
setting
setting
up
up
an
an
event
event
handler
handler
here
here
that
that
will
will
just
just
console
console
log
log
the
the
event
event
from
from
our
our
custom
custom
event
event
in
in
the
the
directive.
directive
All
all
we
we
need
need
to
to
do
do
is
is
set
set
up
up
a
a
div
div
and
and
inside
inside
that
that
div
div
we'll
we'll
add
add
the
the
scrollable
scrollable
directive.
directive
And
and
then
then
for
for
the
the
event
event
we
we
use
use
parenthesis
parenthesis
c's
c's
with
with
the
the
scroll
scroll
position
position
event
event
and
and
then
then
we'll
we'll
run
run
our
our
scroll
scroll
handler.
handler
And
and
I'm
i'm
going
going
to
to
add
add
an
an
extra
extra
div
div
in
in
here
here
with
with
some
some
height
height
to
to
it
it
just
just
so
so
we
we
can
can
actually
actually
scroll
scroll
through
through
this
this
parent
parent
div.
div
If
if
you
you
watch
watch
the
the
console
console
to
to
the
the
right,
right
you'll
you'll
see
see
we
we
either
either
get
get
a
a
bottom
bottom
or
or
top
top
event
event
once
once
we've
we've
reached
reached
the
the
bottom
bottom
or
or
top
top
of
of
the
the
element
element
itself.
itself
And
and
before
before
we
we
get
get
to
to
Firestore,
firestore
let's
let's
generate
generate
a
a
component
component
called
called
loading
loading
spinner.
spinner
Then
then
head
head
over
over
to
to
SpinKit
spinkit
and
and
choose
choose
your
your
favorite
favorite
spinner
spinner
and
and
just
just
Copy
copy
and
and
paste
paste
the
the
HTML
html
and
and
CSS
css
into
into
that
that
component.
component
Before
before
we
we
build
build
this
this
big
big
complex
complex
pagination
pagination
service,
service
I
i
wanna
wanna
show
show
you
you
how
how
it's
it's
going
going
to
to
be
be
used
used
in
in
the
the
component.
component
To
to
paginate
paginate
in
in
Firestore,
firestore
you
you
need
need
to
to
send
send
a
a
new
new
query
query
for
for
each
each
new
new
batch
batch
of
of
data
data
that
that
you
you
need.
need
The
the
service
service
I'm
i'm
creating
creating
will
will
allow
allow
you
you
to
to
make
make
a
a
Query
query
with
with
some
some
basic
basic
configuration
configuration
options,
options
and
and
this
this
will
will
populate
populate
an
an
observable
observable
on
on
the
the
service
service
that
that
we
we
can
can
subscribe
subscribe
to.
to
The
the
first
first
argument
argument
to
to
init
init
is
is
the
the
collection
collection
and
and
the
the
second
second
is
is
the
the
field
field
that
that
you
you
wanna
wanna
order
order
by.
by
You
you
can
can
order
order
the
the
collection
collection
in
in
reverse
reverse
And
and
you
you
can
can
also
also
prepend
prepend
new
new
items
items
to
to
the
the
beginning
beginning
which
which
is
is
what
what
you
you
would
would
use
use
if
if
you
you
were
were
doing
doing
an
an
upward
upward
infinite
infinite
scroll.
scroll
After
after
the
the
initial
initial
query,
query
we'll
we'll
listen
listen
for
for
events
events
in
in
our
our
scroll
scroll
handler.
handler
Once
once
we
we
receive
receive
the
the
bond
bond
event,
event
we
we
can
can
just
just
call
call
the
the
more
more
method
method
from
from
the
the
service
service
and
and
that
that
will
will
populate
populate
the
the
next
next
batch
batch
of
of
items
items
from
from
Firestore.
firestore
So
so
the
the
service
service
we're
we're
building
building
will
will
give
give
you
you
infinite
infinite
scroll
scroll
with
with
just
just
two
two
lines
lines
of
of
code.
code
The
the
service
service
will
will
also
also
give
give
us
us
a
a
data
data
variable
variable
that's
that's
an
an
observable
observable
array
array
that
that
we
we
can
can
loop
loop
over
over
with
with
the
the
async
async
pipe.
pipe
So
so
we'll
we'll
display
display
some
some
information
information
about
about
our
our
boats
boats
in
in
the
the
database,
database
but
but
the
the
service
service
also
also
has
has
2
2
other
other
variables
variables
that
that
are
are
important.
important
The
the
first
first
one
one
will
will
tell
tell
us
us
once
once
we've
we've
reached
reached
the
the
end
end
of
of
a
a
collection
collection
and
and
that's
that's
the
the
done
done
variable.
variable
So
so
we
we
can
can
listen
listen
for
for
that
that
and
and
then
then
say
say
I
i
ran
ran
out
out
of
of
boats
boats
once
once
we
we
reach
reach
the
the
end.
end
We
we
have
have
another
another
observable
observable
that
that
will
will
tell
tell
us
us
whether
whether
or
or
not
not
a
a
new
new
query
query
is
is
loading
loading
so
so
we
we
can
can
show
show
a
a
loading
loading
spinner
spinner
for
for
each
each
new
new
query,
query
and
and
that
that
also
also
helps
helps
us
us
prevent
prevent
sending
sending
multiple
multiple
unnecessary
unnecessary
queries.
queries
If
if
we
we
take
take
a
a
look
look
in
in
Firestore
firestore
quickly,
quickly
we
we
just
just
have
have
a
a
boats
boats
collection
collection
that's
that's
in
in
the
the
root,
root
and
and
each
each
document
document
has
has
a
a
URL
url
and
and
a
a
year
year
field.
field
So
so
we'll
we'll
be
be
sorting
sorting
by
by
the
the
year.
year
When
when
we
we
look
look
in
in
the
the
app
app
we
we
get
get
the
the
initial
initial
loading
loading
spinner
spinner
and
and
then
then
once
once
we
we
scroll
scroll
down
down
the
the
loading
loading
spinner
spinner
will
will
reappear
reappear
And
and
about
about
a
a
second
second
later,
later
we'll
we'll
get
get
some
some
new
new
data
data
loaded.
loaded
Now
now
we're
we're
finally
finally
ready
ready
to
to
start
start
building
building
the
the
service.
service
We're
we're
going
going
to
to
need
need
Angular
angular
Firestore
firestore
And
and
we're
we're
also
also
going
going
to
to
make
make
pretty
pretty
heavy
heavy
usage
usage
of
of
RxJS
rxjs
behavior
behavior
subjects.
subjects
Next,
next
I'm
i'm
going
going
to
to
set
set
up
up
an
an
interface
interface
that
that
defines
defines
the
the
query
query
configuration
configuration
options.
options
So
so
we
we
have
have
a
a
required
required
field
field
of
of
path
path
which
which
is
is
the
the
path
path
to
to
your
your
collection
collection
and
and
then
then
we
we
have
have
the
the
field
field
that
that
you
you
wanna
wanna
order
order
by.
by
Then
then
we'll
we'll
set
set
default
default
values
values
for
for
limit,
limit
reverse,
reverse
and
and
Penned.
penned
Prepend's
prepend's
the
the
important
important
one.
one
That's
that's
the
the
one
one
that
that
you
you
would
would
use
use
if
if
you
you
wanted
wanted
to
to
have
have
the
the
items
items
at
at
the
the
beginning
beginning
of
of
the
the
array
array
instead
instead
of
of
added
added
to
to
the
the
end
end
of
of
the
the
array.
array
Next,
next
I'm
i'm
going
going
to
to
set
set
up
up
a
a
few
few
behavior
behavior
subjects.
subjects
The
the
subjects
subjects
are
are
all
all
private
private
variables
variables
and
and
they
they
have
have
underscores
underscores
so
so
you
you
can
can
keep
keep
track
track
of
of
them
them
easily
easily
in
in
the
the
code.
code
Done
done
will
will
tell
tell
us
us
once
once
we've
we've
reached
reached
the
the
end
end
of
of
the
the
collection.
collection
Loading
loading
will
will
tell
tell
us
us
when
when
a
a
query
query
is
is
in
in
process,
process
And
and
data
data
is
is
the
the
last
last
batch
batch
of
of
information
information
that
that
has
has
been
been
retrieved
retrieved
from
from
Firebase.
firebase
Then
then
we'll
we'll
set
set
the
the
query
query
configuration
configuration
as
as
a
a
variable
variable
so
so
we
we
can
can
keep
keep
track
track
of
of
it
it
between
between
queries.
queries
And
and
that'll
that'll
be
be
typed
typed
to
to
our
our
Query
query
config
config
interface.
interface
Then
then
for
for
each
each
of
of
these
these
subjects,
subjects
we're
we're
going
going
to
to
set
set
an
an
observable
observable
that
that
will
will
be
be
consumed
consumed
by
by
the
the
component.
component
This
this
will
will
keep
keep
all
all
of
of
the
the
data
data
changes
changes
isolated
isolated
to
to
the
the
service.
service
This
this
part's
part's
technically
technically
optional,
optional
but
but
it
it
is
is
considered
considered
a
a
good
good
practice.
practice
All
all
we
we
need
need
to
to
do
do
is
is
call
call
as
as
observable
observable
on
on
the
the
source
source
data.
data
Then
then
the
the
next
next
step
step
is
is
to
to
add
add
AngularFirestore
angularfirestore
to
to
the
the
constructor.
constructor
Then
then
we're
we're
gonna
gonna
go
go
through
through
a
a
lot
lot
of
of
code
code
here,
here
so
so
I
i
recommend
recommend
checking
checking
out
out
the
the
main
main
article
article
for
for
detailed
detailed
explanations
explanations
on
on
each
each
of
of
these
these
methods.
methods
The
the
first
first
one
one
we'll
we'll
set
set
up
up
is
is
init,
init
which
which
is
is
going
going
to
to
make
make
an
an
initial
initial
query
query
as
as
well
well
as
as
save
save
our
our
query
query
configuration
configuration
on
on
the
the
service.
service
It
it
requires
requires
a
a
path
path
to
to
the
the
collection
collection
as
as
well
well
as
as
a
a
field
field
to
to
order
order
by
by
and
and
then
then
it
it
has
has
optional
optional
defaults
defaults
here
here
that
that
you
you
can
can
override
override
by
by
passing
passing
them
them
as
as
an
an
object.
object
So
so
we'll
we'll
set
set
a
a
default
default
limit
limit
of
of
2
2
and
and
then
then
say
say
reverse
reverse
false
false
and
and
prepend
prepend
false.
false
Now
now
we
we
can
can
use
use
this
this
object
object
to
to
make
make
a
a
reference
reference
to
to
the
the
Firestore
firestore
collection.
collection
We're
we're
going
going
to
to
order
order
by
by
the
the
query
query
field.
field
If
if
the
the
reverse
reverse
property
property
is
is
set
set
to
to
true,
true
then
then
we'll
we'll
go
go
ahead
ahead
and
and
order
order
it
it
descending.
descending
Otherwise,
otherwise
it'll
it'll
be
be
ascending.
ascending
Then
then
we'll
we'll
go
go
ahead
ahead
and
and
limit
limit
it
it
by
by
whatever
whatever
is
is
set
set
in
in
the
the
query
query
configuration.
configuration
Then
then
I
i
have
have
a
a
map
map
and
and
update
update
method
method
that
that
I'm
i'm
going
going
to
to
show
show
you
you
in
in
just
just
a
a
second
second
here.
here
But
but
first,
first
I'm
i'm
gonna
gonna
show
show
you
you
how
how
we
we
and
and
the
the
observable
observable
data
data
that
that
will
will
actually
actually
be
be
used
used
in
in
the
the
front
front
end
end
component.
component
So
so
we'll
we'll
take
take
our
our
source
source
data,
data
and
and
then
then
we'll
we'll
call
call
the
the
RxJS
rxjs
scan
scan
operator
operator
on
on
it.
it
Scan
scan
allows
allows
us
us
to
to
build
build
a
a
larger
larger
array
array
over
over
time.
time
If
if
the
the
query
query
options
options
are
are
set
set
to
to
prepend,
prepend
then
then
we
we
wanna
wanna
take
take
the
the
new
new
value
value
and
and
concatenate
concatenate
it
it
to
to
the
the
beginning
beginning
of
of
the
the
array.
array
And
and
if
if
it's
it's
not
not
set,
set
then
then
we
we
just
just
wanna
wanna
go
go
ahead
ahead
and
and
concatenate
concatenate
it
it
to
to
the
the
end.
end
Scan
scan
is
is
a
a
pretty
pretty
weird
weird
operator
operator
to
to
learn,
learn
so
so
I
i
recommend
recommend
going
going
to
to
RxJS
rxjs
Marbles
marbles
and
and
checking
checking
it
it
out
out
visually.
visually
Now
now
let's
let's
take
take
a
a
look
look
at
at
that
that
map
map
and
and
update
update
method.
method
It
it
takes
takes
the
the
AngularFirestore
angularfirestore
collection
collection
and
and
then
then
maps
maps
it
it
down
down
to
to
the
the
snapshot
snapshot
that
that
we
we
need
need
and
and
then
then
Sends
sends
it
it
to
to
our
our
main
main
data
data
source.
source
If
if
the
the
collection
collection
is
is
already
already
done
done
or
or
loading,
loading
we
we
don't
don't
wanna
wanna
make
make
the
the
query
query
so
so
we'll
we'll
just
just
return
return
out
out
of
of
the
the
function.
function
But,
but
otherwise,
otherwise
we'll
we'll
set
set
the
the
loading
loading
state
state
to
to
true,
true
and
and
then
then
we'll
we'll
call
call
collection
collection
snapshot
snapshot
changes.
changes
To
to
paginate
paginate
Cursor
cursor
in
in
Firestore,
firestore
you
you
have
have
to
to
use
use
the
the
document
document
snapshot.
snapshot
So
so
what
what
we're
we're
going
going
to
to
do
do
is
is
map
map
down
down
the
the
array
array
to
to
the
the
data
data
that
that
we
we
wanna
wanna
show
show
on
on
the
the
front
front
end
end
as
as
well
well
as
as
the
the
snapshot.
snapshot
You'll
you'll
see
see
in
in
a
a
second
second
here
here
how
how
we
we
use
use
the
the
snapshot
snapshot
to
to
offset
offset
all
all
future
future
queries.
queries
Then
then
after
after
we
we
have
have
this
this
data
data
mapped
mapped
down,
down
we're
we're
going
going
to
to
check
check
and
and
see
see
if
if
the
the
prepend
prepend
option
option
is
is
set.
set
Then
then
we
we
need
need
to
to
reverse
reverse
Each
each
individual
individual
batch
batch
to
to
maintain
maintain
a
a
consistent
consistent
order
order
going
going
in
in
reverse.
reverse
There
there
may
may
be
be
a
a
better
better
way
way
to
to
do
do
this,
this
but
but
I
i
wasn't
wasn't
able
able
to
to
figure
figure
one
one
out
out
for
for
the
the
upward
upward
Scroll
scroll
when
when
you're
you're
prepending
prepending
new
new
items
items
to
to
a
a
list.
list
After
after
we
we
have
have
the
the
values
values
mapped
mapped
down,
down
we'll
we'll
go
go
ahead
ahead
and
and
update
update
them
them
in
in
our
our
main
main
data
data
source.
source
At
at
this
this
point,
point
we
we
can
can
set
set
our
our
loading
loading
state
state
to
to
false
false
and
and
we
we
can
can
also
also
check
check
to
to
see
see
if
if
that
that
was
was
an
an
empty
empty
array,
array
at
at
which
which
point
point
we
we
know
know
we
we
have
have
reached
reached
the
the
end
end
of
of
the
the
collection.
collection
And
and
this
this
is
is
going
going
to
to
be
be
a
a
one
one
off
off
operation
operation
for
for
each
each
new
new
query,
query
so
so
we're
we're
going
going
to
to
say
say
take
take
1
1
and
and
subscribe.
subscribe
This
this
will
will
end
end
your
your
real
real
time
time
connection
connection
with
with
Firebase,
firebase
but
but
pagination
pagination
with
with
real
real
time
time
data
data
introduces
introduces
a
a
whole
whole
other
other
host
host
of
of
issues
issues
and
and
trade
trade
offs
offs
that
that
you
you
have
have
to
to
think
think
about.
about
It's
it's
beyond
beyond
the
the
scope
scope
of
of
this
this
video,
video
but
but
please
please
reach
reach
out
out
on
on
Slack
slack
if
if
you
you
wanna
wanna
talk
talk
about
about
it.
it
Now
now
we're
we're
ready
ready
to
to
set
set
up
up
a
a
method
method
to
to
make
make
secondary
secondary
queries.
queries
So
so
first
first
we
we
need
need
to
to
know
know
the
the
cursor
cursor
that
that
we
we
wanna
wanna
paginate
paginate
from.
from
If
if
we're
we're
prepending,
prepending
we
we
want
want
the
the
document
document
snapshot
snapshot
from
from
the
the
first
first
item
item
in
in
the
the
last
last
batch.
batch
Otherwise,
otherwise
we
we
want
want
the
the
last
last
item
item
in
in
the
the
last
last
batch.
batch
If
if
the
the
array
array
is
is
empty,
empty
we
we
want
want
to
to
return
return
null.
null
Otherwise,
otherwise
we
we
will
will
get
get
an
an
error
error
from
from
Firestore.
firestore
The
the
more
more
method
method
is
is
going
going
to
to
make
make
all
all
subsequent
subsequent
queries.
queries
First,
first
we
we
need
need
to
to
get
get
the
the
cursor
cursor
for
for
the
the
pagination,
pagination
and
and
then
then
we'll
we'll
create
create
a
a
Firestore
firestore
collection
collection
reference.
reference
It's
it's
identical
identical
to
to
the
the
initial
initial
query
query
except
except
that
that
it
it
uses
uses
start
start
after
after
and
and
passing
passing
that
that
cursor
cursor
as
as
an
an
argument.
argument
This
this
tells
tells
Firestore
firestore
where
where
to
to
offset
offset
the
the
query
query
from.
from
Then
then
once
once
we
we
have
have
that
that
set,
set
we
we
can
can
call
call
map
map
an
an
update,
update
and
and
our
our
data
data
source
source
will
will
be
be
updated.
updated
That
that
was
was
a
a
lot
lot
of
of
code
code
to
to
get
get
through
through
in
in
10
10
minutes,
minutes
but
but
make
make
sure
sure
to
to
check
check
out
out
the
the
full
full
article
article
for
for
detailed
detailed
explanations
explanations
on
on
all
all
these
these
methods.
methods
My
my
goal
goal
is
is
to
to
give
give
you
you
a
a
generic
generic
service
service
that
that
can
can
work
work
with
with
any
any
Firestore
firestore
collection
collection
and
and
give
give
you
you
infinite
infinite
scroll
scroll
either
either
upwards
upwards
or
or
downwards.
downwards
That's
that's
it
it
for
for
Firestore
firestore
Infinite
infinite
Scroll
scroll
with
with
Angular.
angular
If
if
this
this
video
video
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you
you
want
want
twice
twice
as
as
much
much
content
content
every
every
week,
week
Consider
consider
becoming
becoming
a
a
pro
pro
member
member
at
at
angularfirebase.com.
angularfirebase.com
You'll
you'll
get
get
exclusive
exclusive
content,
content
a
a
free
free
copy
copy
of
of
my
my
book,
book
and
and
1
1
on
on
1
1
project
project
consulting.
consulting
Thanks
thanks
for
for
watching,
watching
and
and
I'll
i'll
see
see
you
you
next
next
time.
time
success
The speakers discuss implementing infinite scroll in Angular and setting up a try catch block for responsive components. They also explain how to set up an event handler and generate a loading spinner for each new query. They stress the importance of setting up query configuration and updating the load state for the front end component, and mention a free copy of a book and a project consulting program. They emphasize the need to map and update method and use RxJS scan operator.
Infinite Scroll Pagination with Firestore
Infinite Scroll is a feature that can have a huge impact on the user experience by loading data asynchronously based on scroll position. In In this video, I'm going to show you how to implement infinite scroll in Angular with the Firestore database, but we're going to do it in a way that not only allows you to scroll down to load content, but also to scroll up and load content in the opposite direction. This has been a commonly requested feature and it would be used if you were doing something like Facebook Messenger or Slack where you load a timeline of messages in reverse. We're going to accomplish this by creating a generic service in Angular and it will not only retrieve the data, but also tell us when we've reached the end of a collection, and when we are loading new data. If you're new to the channel, make sure to subscribe and grab the source code for this project from angularfirebase.com. I'm going to be starting from a brand new Angular app and its only dependency is AngularFire, so make sure to go to the documentation and follow the setup instructions there. Then the next step is to create a directive called scrollable. This is going to tell us whether or not the user has scrolled to either the top or bottom of the page. We're going to do that by monitoring the scroll event on the element itself. That'd be the container element. And then we're going to emit our own custom event that It will tell us whether or not it's the bottom or top. You can create a custom event in Angular by using the Output decorator and Event emitter. So our custom event here is named scroll position. Then I'm going to add ElementRef to the constructor. But I want to warn you that using ElementRef for direct DOM access can cause issues in Angular with platform server and web worker. We can address this issue by wrapping native elements in a try catch Block, then provide a fallback such as a load more button when the DOM doesn't actually exist. First, we'll set up a host listener on the element, Then listen to the scroll event, so keep in mind that your host element needs to be something scrollable. Then we'll set up a function here and put our try catch block inside Then we need 3 values from the native element itself. We need scroll top, scroll height, and offset height. First, we need to know if the user has reached the bottom of the element. So we can do that by saying top greater than height minus offset minus one extra pixel. When that condition is met, we can have our event emitter send out a string value of bottom. For top, we can simply see if the scroll top is at 0. Before we go any further, let's make sure this works in an actual component. So I'm going to switch over to the app component, then I'm going to import observable as well as the pagination service, which we're going to create in the next step. Then I am setting up an event handler here that will just console log the event from our custom event in the directive. All we need to do is set up a div and inside that div we'll add the scrollable directive. And then for the event we use parenthesis c's with the scroll position event and then we'll run our scroll handler. And I'm going to add an extra div in here with some height to it just so we can actually scroll through this parent div. If you watch the console to the right, you'll see we either get a bottom or top event once we've reached the bottom or top of the element itself. And before we get to Firestore, let's generate a component called loading spinner. Then head over to SpinKit and choose your favorite spinner and just Copy and paste the HTML and CSS into that component. Before we build this big complex pagination service, I wanna show you how it's going to be used in the component. To paginate in Firestore, you need to send a new query for each new batch of data that you need. The service I'm creating will allow you to make a Query with some basic configuration options, and this will populate an observable on the service that we can subscribe to. The first argument to init is the collection and the second is the field that you wanna order by. You can order the collection in reverse And you can also prepend new items to the beginning which is what you would use if you were doing an upward infinite scroll. After the initial query, we'll listen for events in our scroll handler. Once we receive the bond event, we can just call the more method from the service and that will populate the next batch of items from Firestore. So the service we're building will give you infinite scroll with just two lines of code. The service will also give us a data variable that's an observable array that we can loop over with the async pipe. So we'll display some information about our boats in the database, but the service also has 2 other variables that are important. The first one will tell us once we've reached the end of a collection and that's the done variable. So we can listen for that and then say I ran out of boats once we reach the end. We have another observable that will tell us whether or not a new query is loading so we can show a loading spinner for each new query, and that also helps us prevent sending multiple unnecessary queries. If we take a look in Firestore quickly, we just have a boats collection that's in the root, and each document has a URL and a year field. So we'll be sorting by the year. When we look in the app we get the initial loading spinner and then once we scroll down the loading spinner will reappear And about a second later, we'll get some new data loaded. Now we're finally ready to start building the service. We're going to need Angular Firestore And we're also going to make pretty heavy usage of RxJS behavior subjects. Next, I'm going to set up an interface that defines the query configuration options. So we have a required field of path which is the path to your collection and then we have the field that you wanna order by. Then we'll set default values for limit, reverse, and Penned. Prepend's the important one. That's the one that you would use if you wanted to have the items at the beginning of the array instead of added to the end of the array. Next, I'm going to set up a few behavior subjects. The subjects are all private variables and they have underscores so you can keep track of them easily in the code. Done will tell us once we've reached the end of the collection. Loading will tell us when a query is in process, And data is the last batch of information that has been retrieved from Firebase. Then we'll set the query configuration as a variable so we can keep track of it between queries. And that'll be typed to our Query config interface. Then for each of these subjects, we're going to set an observable that will be consumed by the component. This will keep all of the data changes isolated to the service. This part's technically optional, but it is considered a good practice. All we need to do is call as observable on the source data. Then the next step is to add AngularFirestore to the constructor. Then we're gonna go through a lot of code here, so I recommend checking out the main article for detailed explanations on each of these methods. The first one we'll set up is init, which is going to make an initial query as well as save our query configuration on the service. It requires a path to the collection as well as a field to order by and then it has optional defaults here that you can override by passing them as an object. So we'll set a default limit of 2 and then say reverse false and prepend false. Now we can use this object to make a reference to the Firestore collection. We're going to order by the query field. If the reverse property is set to true, then we'll go ahead and order it descending. Otherwise, it'll be ascending. Then we'll go ahead and limit it by whatever is set in the query configuration. Then I have a map and update method that I'm going to show you in just a second here. But first, I'm gonna show you how we and the observable data that will actually be used in the front end component. So we'll take our source data, and then we'll call the RxJS scan operator on it. Scan allows us to build a larger array over time. If the query options are set to prepend, then we wanna take the new value and concatenate it to the beginning of the array. And if it's not set, then we just wanna go ahead and concatenate it to the end. Scan is a pretty weird operator to learn, so I recommend going to RxJS Marbles and checking it out visually. Now let's take a look at that map and update method. It takes the AngularFirestore collection and then maps it down to the snapshot that we need and then Sends it to our main data source. If the collection is already done or loading, we don't wanna make the query so we'll just return out of the function. But, otherwise, we'll set the loading state to true, and then we'll call collection snapshot changes. To paginate Cursor in Firestore, you have to use the document snapshot. So what we're going to do is map down the array to the data that we wanna show on the front end as well as the snapshot. You'll see in a second here how we use the snapshot to offset all future queries. Then after we have this data mapped down, we're going to check and see if the prepend option is set. Then we need to reverse Each individual batch to maintain a consistent order going in reverse. There may be a better way to do this, but I wasn't able to figure one out for the upward Scroll when you're prepending new items to a list. After we have the values mapped down, we'll go ahead and update them in our main data source. At this point, we can set our loading state to false and we can also check to see if that was an empty array, at which point we know we have reached the end of the collection. And this is going to be a one off operation for each new query, so we're going to say take 1 and subscribe. This will end your real time connection with Firebase, but pagination with real time data introduces a whole other host of issues and trade offs that you have to think about. It's beyond the scope of this video, but please reach out on Slack if you wanna talk about it. Now we're ready to set up a method to make secondary queries. So first we need to know the cursor that we wanna paginate from. If we're prepending, we want the document snapshot from the first item in the last batch. Otherwise, we want the last item in the last batch. If the array is empty, we want to return null. Otherwise, we will get an error from Firestore. The more method is going to make all subsequent queries. First, we need to get the cursor for the pagination, and then we'll create a Firestore collection reference. It's identical to the initial query except that it uses start after and passing that cursor as an argument. This tells Firestore where to offset the query from. Then once we have that set, we can call map an update, and our data source will be updated. That was a lot of code to get through in 10 minutes, but make sure to check out the full article for detailed explanations on all these methods. My goal is to give you a generic service that can work with any Firestore collection and give you infinite scroll either upwards or downwards. That's it for Firestore Infinite Scroll with Angular. If this video helped you, please like and subscribe. And if you want twice as much content every week, Consider becoming a pro member at angularfirebase.com. You'll get exclusive content, a free copy of my book, and 1 on 1 project consulting. Thanks for watching, and I'll see you next time.
The speakers discuss implementing infinite scroll in Angular and setting up a try catch block for responsive components. They also explain how to set up an event handler and generate a loading spinner for each new query. They stress the importance of setting up query configuration and updating the load state for the front end component, and mention a free copy of a book and a project consulting program. They emphasize the need to map and update method and use RxJS scan operator.