Learn how to use the new Virtual Scroll behavior in Angular CDK v7 to build a list that is infinite, animated, and realtime with Firestore. https://angularfirebase.com/lessons/infinite-virtual-scroll-angular-cdk/ 

- CDK Docs https://material.angular.io/cdk/scrolling/overview
- Firestore https://cloud.google.com/firestore/docs/
2024-02-04T07:10:11.448Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
b0f2915f-8e59-41d1-9107-1f2389d4f053
d22074e5e958596d01d86cf0843cfc48178b4e6d358c8adbb34e7df378778763
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
Angular 7 is officially here and one of its coolest new features is the virtual scroll behavior in the component development kit.
It allows you to easily build a high performance list that only renders the items visible to the user.
In today's video, I'll show you everything you need to know about virtual scroll, but we'll also take things a few steps further by making it animated, infinite, and real time.
If you're new here, like and subscribe and I'm giving away some winter swag with this video.
All you have to do is leave a comment below and we'll pick a winner on the live stream on Wednesday.
So first of all, what is Virtual scrolling and why would you want to use it?
It's useful when you have a large list of items to loop over, but you only want to render the items that are visible in the viewport.
This should give you much smoother performance when working with big lists because the browser only needs to render a small subset of the data.
The CDK provides a handful of components and directives to make virtual scrolling super easy.
In addition, it provides a handful of tools that makes it much easier to measure the scrolled element, which we can then use to pull data from our back end database in batches, giving us an infinite real time virtual scroll.
The first thing you'll wanna do is make sure you're up to date with Angular version 7.
Then we can quickly add the CDK to our app by running ng add angularmaterial.
You'll notice that when you do this in Angular 7, it gives you a prompt of questions to go through.
You can just select the defaults and then if you also want to follow along with the Firebase part, you'll want to install AngularFire at this point as well.
But I'm going to start with just the CDK basics before we get to that point.
If we jump over to my AppModule, you can see that I'm importing the scrolling module from the CDK.
And then I also have a couple of components generated just for this demo.
So you wanna make sure that you have the scrolling module here in the import section and then optionally AngularFire for the back end stuff.
Now that we're all set up, let's start by looking at the basics of Virtual Scroll.
The first thing you'll need is a big list of something a loop over, so I wanna show you how to generate a ton of fake data really quickly.
1st, I wrote my own helper function that will return a random emoji.
And then I installed the Faker JS library that we can use to generate fake names and email addresses and stuff like that.
Then I'll set up a property of people, and then I'll define that property as Array of 100 elements, then we can map each element in that Array to an object with some fake data.
So we'll give each object a name, a bio, and an emoji.
So that gives us a big array of items to loop over.
Now we need to go into the HTML and set up a Context for our virtual scroll.
We can do that by just dropping in the CDK Virtual Scroll Viewport component.
It has one required argument which is the item size or height which is represented in pixels.
So typically with a virtual scroll, you want items to have Fixed height if possible.
So with that being said, let's quickly jump over to our CSS code.
You'll first wanna make sure that your Viewport has some height.
So in this case, I'm having it fill up the entire height of the Viewport.
Then each item inside of my Viewport will have a fixed height of 100 pixels, which matches the item size argument in the component itself.
And just for bonus points, if you want to make it look cool, this is a good place to customize the pseudo selector for the actual ScrollBar element to give it a different style and size as the user scrolls down.
When inside the context of a ScrollViewport, The CDK gives you a structural directive called CDK virtual 4.
It replaces ng 4, but it works exactly the same way.
If we go ahead and serve the app, what's happening is that the CDK is keeping track of the items in the list and which items will fit into the viewport.
A good way to demonstrate this is just by using a plain CSS animation because that animation will be applied each time the item is re rendered.
So I'm using a little library here you may have heard of called Animate CSS.
And you'll notice as I scroll up and down, the animation is reapplied to each element because it's being re rendered in the DOM.
In a regular list, the CSS animation would only be applied on the initial page load.
So that's kind of an interesting little side effect with VirtualScroll that you can play with.
Now there's a couple of other basic things that you need to know.
First of all, the virtual scroll component emits a custom event called Scrolled Index Change.
Eventually, we'll use this to implement our infinite Scroll because it emits the index of the item that the user has scrolled to, so we can use that to determine when to pull the next batch of items from the database.
Then you'll also notice that this component has its own public API that we can use to do things like measure the viewport or get the total item list size or even dynamically scroll to a specific element.
In order to use those methods, you'll need to grab the component from the DOM.
And you'll definitely want to strong type it, so we'll import the CDK Virtual Scroll Viewport component And then we can grab it with ViewChild.
Then we can set it up as a property called Viewport.
And if we start typing this out, you'll notice that it shows us a bunch of different methods that we can use.
So that's how you can interact with the component dynamically in your TypeScript code.
For example, if you have some chat messages, you could use this to scroll directly to bottom or update the scroll position when new messages are received.
Now that you know the basics, we're ready to build out our Infinite Real Time Scroll using Firestore.
I created an Infinite Scroll video with Firestore a while back, but today's solution will improve upon it in 2 big ways.
First of all, it will make the list real time.
So even though we're pulling items in batches, they'll still be updated in real time if the data changes on the back end.
And the second obvious improvement is that this will be a virtual scroll, so we should be able to get much better performance as the user scrolls through more and more items.
And also the CDK will just make our code much more simple and reliable.
Now starting in the component TypeScript, we'll first import AngularFirestore, as well as a bunch of different stuff here from RxJS.
We'll pull items from the database in batch sizes of 20.
Then we'll set up a property for the viewport itself and then one to determine whether or not we've reached the end of the list.
And then a behavior subject to keep track of the offset that we'll use to make our query to Firestore.
And it's important that the subject starts with null and not undefined or an empty string because we need that null value to start our query to Firestore.
So every time this offset changes, we're going to wanna make a new query to Firestore for the source data.
But we only want to do that when the user scrolls to the bottom of the current list.
So we can listen to that index change event that I showed you earlier And then we'll run this next batch method which takes the event as well as the very last item in the list as its argument.
Then if we've reached the end, then we'll just go ahead and return and pull any additional data.
But otherwise, we can take the Viewport rendered range end value and compare that to the total items in the actual list itself.
And conveniently, the CDK has a method for that called Get Data Length.
So if the ending index equals the total length of the items in the array, then we know that we've reached the end.
We can send the next Offset value to our behavior subject, which will reactively pull a new batch of items from the database.
And then while I have you here, I'm also going to set up a Track by function which just tracks the index of the items in the Array and only renders the ones that change.
That's just a good thing to have for real time data because, otherwise, The entire list would rerender for each item that changes.
So now I'm going to break this down into manageable chunks.
The first one is getting an individual batch from the database.
And to do that, we need to know the last seen item in the previous batch.
I've added 200 records to Firestore of the same dummy data that I showed you earlier.
And I want to do a paginated query on that data.
So first I want to order by the user's name and then we'll start after the offset value, which needs to be null if we're starting a new batch.
Otherwise, it needs to be the name of the last user and the last batch.
And lastly, we'll limit our query to our batch size which again is 20.
From there, we'll go ahead and call Snapshot Changes to get these Document Snapshots as an observable.
If this observable emits an empty array, then it's safe to assume that we've reached the end of our collection in the database, at which point we can set the end property to true.
Now the tricky part is keeping everything up to date in real time across multiple batches.
In order to do that reliably, we need to take the array of documents and then map them to an object where the key is their document ID.
D.
This allows us to update a document's data anywhere in the list based on its key value instead of its index position.
This ensures that Every document in the Array is unique because otherwise we are at risk of adding duplicate documents to the Array if things change between the Queries.
So the next thing we wanna do is go into our constructor and set up a source observable that first listens to the offset value and then merges in new batches whenever the Offset changes, which again will happen when the user scrolls to the bottom of a list.
That event can be fired multiple times, so I'm going to throttle it by 500 milliseconds just so we're not sending off redundant requests to the back end.
Now you'll notice that I'm using Merge Map here to merge a new batch into the stream.
It's important to use Merge Map here and not Switch Map because otherwise the previous subscriptions would be canceled, meaning that only the very last batch would be real time.
From there, we can pipe in the Scan operator, which will just merge in the current batch with the existing batches.
So this Batch Map Observable is our main source of truth because it contains all of the mappings for the individual item keys and their data.
But we still want an actual array to loop over in the frontend.
So we can go here and define our actual infinite array of objects as this batch map, but mapped to the object values which will give us an array of objects.
And that takes care of the main logic for our Infinite Real Time Virtual Scroll.
That may have seemed relatively complex, but in the grand scheme of things it's very simple considering what it does.
Now we just have a couple of small modifications in the HTML.
If you want to show an index in your list, you can just say let I equals index and then we'll also add in our trackBy function that we defined earlier.
Then we can listen to that scrolledIndexChange event and the first argument will be the event itself, which is the index number.
Then the second argument is the offset value for the query, which in our case will be the name of the very last person in the array.
We can actually just parse that value right here in the template.
Not the prettiest code in the world, but it works.
Then the last thing we need to do is unwrap the infinite observable.
My preferred way of doing this is using an NG Container and then unwrapping the Observable with the AsyncPipe.
We can then say as people to give us a template variable of the array objects and then we can just use it throughout this HTML.
Now if we pull up the demo, you can see that we have all of our objects here in the database And they're queried in the same alphabetical order that we get when we order them on the Firebase console.
And if we make any changes to our data on the right, it will be reflected in the UI on the left in real time.
It is important to keep in mind that the actual position of the item won't change, and if it's deleted, it won't be removed from the list.
But if you need that functionality, that could definitely be made possible with some additional work in the code.
Now, I'm pulling up the browser console, and you'll notice that there's an Array being console logged on the right every time we get to a certain event.
The array size initially starts at 20, and then as we scroll down, it will We'll change the offset and our array grows from 20 to 40 to 60 and so on.
And from the user's perspective, it happened so fast that Looks like this list existed all along, which is exactly what you want with an infinite scroll.
But we are keeping track when we get to the end of the list, so when we finally reach that point we'll go ahead and give gives her some indication that there are no items left to retrieve.
That's all folks.
If this video helps you, please like and subscribe.
And if you wanna take your development the next level, consider becoming a pro member at angularfirebase.com.
You'll get access to all kinds of exclusive content like full courses and my book designed to help you build ship your app faster.
Thanks for watching, and I'll talk to you soon.

Speaker 0: Angular 7 is officially here and one of its coolest new features is the virtual scroll behavior in the component development kit. It allows you to easily build a high performance list that only renders the items visible to the user. In today's video, I'll show you everything you need to know about virtual scroll, but we'll also take things a few steps further by making it animated, infinite, and real time. If you're new here, like and subscribe and I'm giving away some winter swag with this video. All you have to do is leave a comment below and we'll pick a winner on the live stream on Wednesday.

So first of all, what is Virtual scrolling and why would you want to use it? It's useful when you have a large list of items to loop over, but you only want to render the items that are visible in the viewport. This should give you much smoother performance when working with big lists because the browser only needs to render a small subset of the data. The CDK provides a handful of components and directives to make virtual scrolling super easy. In addition, it provides a handful of tools that makes it much easier to measure the scrolled element, which we can then use to pull data from our back end database in batches, giving us an infinite real time virtual scroll.

The first thing you'll wanna do is make sure you're up to date with Angular version 7. Then we can quickly add the CDK to our app by running ng add angularmaterial. You'll notice that when you do this in Angular 7, it gives you a prompt of questions to go through. You can just select the defaults and then if you also want to follow along with the Firebase part, you'll want to install AngularFire at this point as well. But I'm going to start with just the CDK basics before we get to that point.

If we jump over to my AppModule, you can see that I'm importing the scrolling module from the CDK. And then I also have a couple of components generated just for this demo. So you wanna make sure that you have the scrolling module here in the import section and then optionally AngularFire for the back end stuff. Now that we're all set up, let's start by looking at the basics of Virtual Scroll. The first thing you'll need is a big list of something a loop over, so I wanna show you how to generate a ton of fake data really quickly.

1st, I wrote my own helper function that will return a random emoji. And then I installed the Faker JS library that we can use to generate fake names and email addresses and stuff like that. Then I'll set up a property of people, and then I'll define that property as Array of 100 elements, then we can map each element in that Array to an object with some fake data. So we'll give each object a name, a bio, and an emoji. So that gives us a big array of items to loop over.

Now we need to go into the HTML and set up a Context for our virtual scroll. We can do that by just dropping in the CDK Virtual Scroll Viewport component. It has one required argument which is the item size or height which is represented in pixels. So typically with a virtual scroll, you want items to have Fixed height if possible. So with that being said, let's quickly jump over to our CSS code.

You'll first wanna make sure that your Viewport has some height. So in this case, I'm having it fill up the entire height of the Viewport. Then each item inside of my Viewport will have a fixed height of 100 pixels, which matches the item size argument in the component itself. And just for bonus points, if you want to make it look cool, this is a good place to customize the pseudo selector for the actual ScrollBar element to give it a different style and size as the user scrolls down. When inside the context of a ScrollViewport, The CDK gives you a structural directive called CDK virtual 4.

It replaces ng 4, but it works exactly the same way. If we go ahead and serve the app, what's happening is that the CDK is keeping track of the items in the list and which items will fit into the viewport. A good way to demonstrate this is just by using a plain CSS animation because that animation will be applied each time the item is re rendered. So I'm using a little library here you may have heard of called Animate CSS. And you'll notice as I scroll up and down, the animation is reapplied to each element because it's being re rendered in the DOM.

In a regular list, the CSS animation would only be applied on the initial page load. So that's kind of an interesting little side effect with VirtualScroll that you can play with. Now there's a couple of other basic things that you need to know. First of all, the virtual scroll component emits a custom event called Scrolled Index Change. Eventually, we'll use this to implement our infinite Scroll because it emits the index of the item that the user has scrolled to, so we can use that to determine when to pull the next batch of items from the database.

Then you'll also notice that this component has its own public API that we can use to do things like measure the viewport or get the total item list size or even dynamically scroll to a specific element. In order to use those methods, you'll need to grab the component from the DOM. And you'll definitely want to strong type it, so we'll import the CDK Virtual Scroll Viewport component And then we can grab it with ViewChild. Then we can set it up as a property called Viewport. And if we start typing this out, you'll notice that it shows us a bunch of different methods that we can use.

So that's how you can interact with the component dynamically in your TypeScript code. For example, if you have some chat messages, you could use this to scroll directly to bottom or update the scroll position when new messages are received. Now that you know the basics, we're ready to build out our Infinite Real Time Scroll using Firestore. I created an Infinite Scroll video with Firestore a while back, but today's solution will improve upon it in 2 big ways. First of all, it will make the list real time.

So even though we're pulling items in batches, they'll still be updated in real time if the data changes on the back end. And the second obvious improvement is that this will be a virtual scroll, so we should be able to get much better performance as the user scrolls through more and more items. And also the CDK will just make our code much more simple and reliable. Now starting in the component TypeScript, we'll first import AngularFirestore, as well as a bunch of different stuff here from RxJS. We'll pull items from the database in batch sizes of 20.

Then we'll set up a property for the viewport itself and then one to determine whether or not we've reached the end of the list. And then a behavior subject to keep track of the offset that we'll use to make our query to Firestore. And it's important that the subject starts with null and not undefined or an empty string because we need that null value to start our query to Firestore. So every time this offset changes, we're going to wanna make a new query to Firestore for the source data. But we only want to do that when the user scrolls to the bottom of the current list.

So we can listen to that index change event that I showed you earlier And then we'll run this next batch method which takes the event as well as the very last item in the list as its argument. Then if we've reached the end, then we'll just go ahead and return and pull any additional data. But otherwise, we can take the Viewport rendered range end value and compare that to the total items in the actual list itself. And conveniently, the CDK has a method for that called Get Data Length. So if the ending index equals the total length of the items in the array, then we know that we've reached the end.

We can send the next Offset value to our behavior subject, which will reactively pull a new batch of items from the database. And then while I have you here, I'm also going to set up a Track by function which just tracks the index of the items in the Array and only renders the ones that change. That's just a good thing to have for real time data because, otherwise, The entire list would rerender for each item that changes. So now I'm going to break this down into manageable chunks. The first one is getting an individual batch from the database.

And to do that, we need to know the last seen item in the previous batch. I've added 200 records to Firestore of the same dummy data that I showed you earlier. And I want to do a paginated query on that data. So first I want to order by the user's name and then we'll start after the offset value, which needs to be null if we're starting a new batch. Otherwise, it needs to be the name of the last user and the last batch.

And lastly, we'll limit our query to our batch size which again is 20. From there, we'll go ahead and call Snapshot Changes to get these Document Snapshots as an observable. If this observable emits an empty array, then it's safe to assume that we've reached the end of our collection in the database, at which point we can set the end property to true. Now the tricky part is keeping everything up to date in real time across multiple batches. In order to do that reliably, we need to take the array of documents and then map them to an object where the key is their document ID.

D. This allows us to update a document's data anywhere in the list based on its key value instead of its index position. This ensures that Every document in the Array is unique because otherwise we are at risk of adding duplicate documents to the Array if things change between the Queries. So the next thing we wanna do is go into our constructor and set up a source observable that first listens to the offset value and then merges in new batches whenever the Offset changes, which again will happen when the user scrolls to the bottom of a list. That event can be fired multiple times, so I'm going to throttle it by 500 milliseconds just so we're not sending off redundant requests to the back end.

Now you'll notice that I'm using Merge Map here to merge a new batch into the stream. It's important to use Merge Map here and not Switch Map because otherwise the previous subscriptions would be canceled, meaning that only the very last batch would be real time. From there, we can pipe in the Scan operator, which will just merge in the current batch with the existing batches. So this Batch Map Observable is our main source of truth because it contains all of the mappings for the individual item keys and their data. But we still want an actual array to loop over in the frontend.

So we can go here and define our actual infinite array of objects as this batch map, but mapped to the object values which will give us an array of objects. And that takes care of the main logic for our Infinite Real Time Virtual Scroll. That may have seemed relatively complex, but in the grand scheme of things it's very simple considering what it does. Now we just have a couple of small modifications in the HTML. If you want to show an index in your list, you can just say let I equals index and then we'll also add in our trackBy function that we defined earlier.

Then we can listen to that scrolledIndexChange event and the first argument will be the event itself, which is the index number. Then the second argument is the offset value for the query, which in our case will be the name of the very last person in the array. We can actually just parse that value right here in the template. Not the prettiest code in the world, but it works. Then the last thing we need to do is unwrap the infinite observable.

My preferred way of doing this is using an NG Container and then unwrapping the Observable with the AsyncPipe. We can then say as people to give us a template variable of the array objects and then we can just use it throughout this HTML. Now if we pull up the demo, you can see that we have all of our objects here in the database And they're queried in the same alphabetical order that we get when we order them on the Firebase console. And if we make any changes to our data on the right, it will be reflected in the UI on the left in real time. It is important to keep in mind that the actual position of the item won't change, and if it's deleted, it won't be removed from the list.

But if you need that functionality, that could definitely be made possible with some additional work in the code. Now, I'm pulling up the browser console, and you'll notice that there's an Array being console logged on the right every time we get to a certain event. The array size initially starts at 20, and then as we scroll down, it will We'll change the offset and our array grows from 20 to 40 to 60 and so on. And from the user's perspective, it happened so fast that Looks like this list existed all along, which is exactly what you want with an infinite scroll. But we are keeping track when we get to the end of the list, so when we finally reach that point we'll go ahead and give gives her some indication that there are no items left to retrieve.

That's all folks. If this video helps you, please like and subscribe. And if you wanna take your development the next level, consider becoming a pro member at angularfirebase.com. You'll get access to all kinds of exclusive content like full courses and my book designed to help you build ship your app faster. Thanks for watching, and I'll talk to you soon.
Angular 7 is officially here and one of its coolest new features is the virtual scroll behavior in the component development kit. It allows you to easily build a high performance list that only renders the items visible to the user. In today's video, I'll show you everything you need to know about virtual scroll, but we'll also take things a few steps further by making it animated, infinite, and real time. If you're new here, like and subscribe and I'm giving away some winter swag with this video. All you have to do is leave a comment below and we'll pick a winner on the live stream on Wednesday. So first of all, what is Virtual scrolling and why would you want to use it?
video gaming
streaming
animation
multimedia
entertainment
television
It's useful when you have a large list of items to loop over, but you only want to render the items that are visible in the viewport. This should give you much smoother performance when working with big lists because the browser only needs to render a small subset of the data. The CDK provides a handful of components and directives to make virtual scrolling super easy. In addition, it provides a handful of tools that makes it much easier to measure the scrolled element, which we can then use to pull data from our back end database in batches, giving us an infinite real time virtual scroll. The first thing you'll wanna do is make sure you're up to date with Angular version 7. Then we can quickly add the CDK to our app by running ng add angularmaterial. You'll notice that when you do this in Angular 7, it gives you a prompt of questions to go through. You can just select the defaults and then if you also want to follow along with the Firebase part, you'll want to install AngularFire at this point as well. But I'm going to start with just the CDK basics before we get to that point. If we jump over to my AppModule, you can see that I'm importing the scrolling module from the CDK. And then I also have a couple of components generated just for this demo. So you wanna make sure that you have the scrolling module here in the import section and then optionally AngularFire for the back end stuff.
Now that we're all set up, let's start by looking at the basics of Virtual Scroll. The first thing you'll need is a big list of something a loop over, so I wanna show you how to generate a ton of fake data really quickly. 1st, I wrote my own helper function that will return a random emoji.
machine learning
And then I installed the Faker JS library that we can use to generate fake names and email addresses and stuff like that. Then I'll set up a property of people, and then I'll define that property as Array of 100 elements, then we can map each element in that Array to an object with some fake data. So we'll give each object a name, a bio, and an emoji. So that gives us a big array of items to loop over. Now we need to go into the HTML and set up a Context for our virtual scroll. We can do that by just dropping in the CDK Virtual Scroll Viewport component. It has one required argument which is the item size or height which is represented in pixels. So typically with a virtual scroll, you want items to have Fixed height if possible. So with that being said, let's quickly jump over to our CSS code. You'll first wanna make sure that your Viewport has some height. So in this case, I'm having it fill up the entire height of the Viewport. Then each item inside of my Viewport will have a fixed height of 100 pixels, which matches the item size argument in the component itself. And just for bonus points, if you want to make it look cool, this is a good place to customize the pseudo selector for the actual ScrollBar element to give it a different style and size as the user scrolls down. When inside the context of a ScrollViewport, The CDK gives you a structural directive called CDK virtual 4. It replaces ng 4, but it works exactly the same way. If we go ahead and serve the app, what's happening is that the CDK is keeping track of the items in the list and which items will fit into the viewport.
A good way to demonstrate this is just by using a plain CSS animation because that animation will be applied each time the item is re rendered. So I'm using a little library here you may have heard of called Animate CSS. And you'll notice as I scroll up and down, the animation is reapplied to each element because it's being re rendered in the DOM. In a regular list, the CSS animation would only be applied on the initial page load. So that's kind of an interesting little side effect with VirtualScroll that you can play with. Now there's a couple of other basic things that you need to know. First of all, the virtual scroll component emits a custom event called Scrolled Index Change.
animation
graphic design
Eventually, we'll use this to implement our infinite Scroll because it emits the index of the item that the user has scrolled to, so we can use that to determine when to pull the next batch of items from the database. Then you'll also notice that this component has its own public API that we can use to do things like measure the viewport or get the total item list size or even dynamically scroll to a specific element. In order to use those methods, you'll need to grab the component from the DOM. And you'll definitely want to strong type it, so we'll import the CDK Virtual Scroll Viewport component And then we can grab it with ViewChild.
Then we can set it up as a property called Viewport. And if we start typing this out, you'll notice that it shows us a bunch of different methods that we can use. So that's how you can interact with the component dynamically in your TypeScript code. For example, if you have some chat messages, you could use this to scroll directly to bottom or update the scroll position when new messages are received. Now that you know the basics, we're ready to build out our Infinite Real Time Scroll using Firestore.
programming
I created an Infinite Scroll video with Firestore a while back, but today's solution will improve upon it in 2 big ways. First of all, it will make the list real time. So even though we're pulling items in batches, they'll still be updated in real time if the data changes on the back end. And the second obvious improvement is that this will be a virtual scroll, so we should be able to get much better performance as the user scrolls through more and more items.
And also the CDK will just make our code much more simple and reliable. Now starting in the component TypeScript, we'll first import AngularFirestore, as well as a bunch of different stuff here from RxJS. We'll pull items from the database in batch sizes of 20. Then we'll set up a property for the viewport itself and then one to determine whether or not we've reached the end of the list. And then a behavior subject to keep track of the offset that we'll use to make our query to Firestore.
databases
data management
data
And it's important that the subject starts with null and not undefined or an empty string because we need that null value to start our query to Firestore.
So every time this offset changes, we're going to wanna make a new query to Firestore for the source data. But we only want to do that when the user scrolls to the bottom of the current list. So we can listen to that index change event that I showed you earlier And then we'll run this next batch method which takes the event as well as the very last item in the list as its argument.
databases
Then if we've reached the end, then we'll just go ahead and return and pull any additional data. But otherwise, we can take the Viewport rendered range end value and compare that to the total items in the actual list itself. And conveniently, the CDK has a method for that called Get Data Length. So if the ending index equals the total length of the items in the array, then we know that we've reached the end. We can send the next Offset value to our behavior subject, which will reactively pull a new batch of items from the database. And then while I have you here, I'm also going to set up a Track by function which just tracks the index of the items in the Array and only renders the ones that change. That's just a good thing to have for real time data because, otherwise, The entire list would rerender for each item that changes. So now I'm going to break this down into manageable chunks. The first one is getting an individual batch from the database. And to do that, we need to know the last seen item in the previous batch. I've added 200 records to Firestore of the same dummy data that I showed you earlier. And I want to do a paginated query on that data. So first I want to order by the user's name and then we'll start after the offset value, which needs to be null if we're starting a new batch.
databases
programming
data
data management
Otherwise, it needs to be the name of the last user and the last batch. And lastly, we'll limit our query to our batch size which again is 20. From there, we'll go ahead and call Snapshot Changes to get these Document Snapshots as an observable. If this observable emits an empty array, then it's safe to assume that we've reached the end of our collection in the database, at which point we can set the end property to true.
databases
data management
Now the tricky part is keeping everything up to date in real time across multiple batches. In order to do that reliably, we need to take the array of documents and then map them to an object where the key is their document ID.
D. This allows us to update a document's data anywhere in the list based on its key value instead of its index position. This ensures that Every document in the Array is unique because otherwise we are at risk of adding duplicate documents to the Array if things change between the Queries.
databases
So the next thing we wanna do is go into our constructor and set up a source observable that first listens to the offset value and then merges in new batches whenever the Offset changes, which again will happen when the user scrolls to the bottom of a list. That event can be fired multiple times, so I'm going to throttle it by 500 milliseconds just so we're not sending off redundant requests to the back end. Now you'll notice that I'm using Merge Map here to merge a new batch into the stream. It's important to use Merge Map here and not Switch Map because otherwise the previous subscriptions would be canceled, meaning that only the very last batch would be real time. From there, we can pipe in the Scan operator, which will just merge in the current batch with the existing batches. So this Batch Map Observable is our main source of truth because it contains all of the mappings for the individual item keys and their data. But we still want an actual array to loop over in the frontend. So we can go here and define our actual infinite array of objects as this batch map, but mapped to the object values which will give us an array of objects. And that takes care of the main logic for our Infinite Real Time Virtual Scroll. That may have seemed relatively complex, but in the grand scheme of things it's very simple considering what it does. Now we just have a couple of small modifications in the HTML. If you want to show an index in your list, you can just say let I equals index and then we'll also add in our trackBy function that we defined earlier. Then we can listen to that scrolledIndexChange event and the first argument will be the event itself, which is the index number. Then the second argument is the offset value for the query, which in our case will be the name of the very last person in the array. We can actually just parse that value right here in the template. Not the prettiest code in the world, but it works. Then the last thing we need to do is unwrap the infinite observable.
My preferred way of doing this is using an NG Container and then unwrapping the Observable with the AsyncPipe. We can then say as people to give us a template variable of the array objects and then we can just use it throughout this HTML. Now if we pull up the demo, you can see that we have all of our objects here in the database And they're queried in the same alphabetical order that we get when we order them on the Firebase console.
databases
And if we make any changes to our data on the right, it will be reflected in the UI on the left in real time. It is important to keep in mind that the actual position of the item won't change, and if it's deleted, it won't be removed from the list. But if you need that functionality, that could definitely be made possible with some additional work in the code. Now, I'm pulling up the browser console, and you'll notice that there's an Array being console logged on the right every time we get to a certain event. The array size initially starts at 20, and then as we scroll down, it will We'll change the offset and our array grows from 20 to 40 to 60 and so on. And from the user's perspective, it happened so fast that Looks like this list existed all along, which is exactly what you want with an infinite scroll. But we are keeping track when we get to the end of the list, so when we finally reach that point we'll go ahead and give gives her some indication that there are no items left to retrieve.
That's all folks. If this video helps you, please like and subscribe. And if you wanna take your development the next level, consider becoming a pro member at angularfirebase.com. You'll get access to all kinds of exclusive content like full courses and my book designed to help you build ship your app faster. Thanks for watching, and I'll talk to you soon.
software
youtube
software development
microsoft
streaming
publishing
Angular 7 is officially here and one of its coolest new features is the virtual scroll behavior in the component development kit. It allows you to easily build a high performance list that only renders the items visible to the user. In today's video, I'll show you everything you need to know about virtual scroll, but we'll also take things a few steps further by making it animated, infinite, and real time. If you're new here, like and subscribe and I'm giving away some winter swag with this video. All you have to do is leave a comment below and we'll pick a winner on the live stream on Wednesday. So first of all, what is Virtual scrolling and why would you want to use it? It's useful when you have a large list of items to loop over, but you only want to render the items that are visible in the viewport. This should give you much smoother performance when working with big lists because the browser only needs to render a small subset of the data. The CDK provides a handful of components and directives to make virtual scrolling super easy. In addition, it provides a handful of tools that makes it much easier to measure the scrolled element, which we can then use to pull data from our back end database in batches, giving us an infinite real time virtual scroll. The first thing you'll wanna do is make sure you're up to date with Angular version 7. Then we can quickly add the CDK to our app by running ng add angularmaterial. You'll notice that when you do this in Angular 7, it gives you a prompt of questions to go through. You can just select the defaults and then if you also want to follow along with the Firebase part, you'll want to install AngularFire at this point as well. But I'm going to start with just the CDK basics before we get to that point. If we jump over to my AppModule, you can see that I'm importing the scrolling module from the CDK. And then I also have a couple of components generated just for this demo. So you wanna make sure that you have the scrolling module here in the import section and then optionally AngularFire for the back end stuff. Now that we're all set up, let's start by looking at the basics of Virtual Scroll. The first thing you'll need is a big list of something a loop over, so I wanna show you how to generate a ton of fake data really quickly. 1st, I wrote my own helper function that will return a random emoji. And then I installed the Faker JS library that we can use to generate fake names and email addresses and stuff like that. Then I'll set up a property of people, and then I'll define that property as Array of 100 elements, then we can map each element in that Array to an object with some fake data. So we'll give each object a name, a bio, and an emoji. So that gives us a big array of items to loop over. Now we need to go into the HTML and set up a Context for our virtual scroll. We can do that by just dropping in the CDK Virtual Scroll Viewport component. It has one required argument which is the item size or height which is represented in pixels. So typically with a virtual scroll, you want items to have Fixed height if possible. So with that being said, let's quickly jump over to our CSS code. You'll first wanna make sure that your Viewport has some height. So in this case, I'm having it fill up the entire height of the Viewport. Then each item inside of my Viewport will have a fixed height of 100 pixels, which matches the item size argument in the component itself. And just for bonus points, if you want to make it look cool, this is a good place to customize the pseudo selector for the actual ScrollBar element to give it a different style and size as the user scrolls down. When inside the context of a ScrollViewport, The CDK gives you a structural directive called CDK virtual 4. It replaces ng 4, but it works exactly the same way. If we go ahead and serve the app, what's happening is that the CDK is keeping track of the items in the list and which items will fit into the viewport. A good way to demonstrate this is just by using a plain CSS animation because that animation will be applied each time the item is re rendered. So I'm using a little library here you may have heard of called Animate CSS. And you'll notice as I scroll up and down, the animation is reapplied to each element because it's being re rendered in the DOM. In a regular list, the CSS animation would only be applied on the initial page load. So that's kind of an interesting little side effect with VirtualScroll that you can play with. Now there's a couple of other basic things that you need to know. First of all, the virtual scroll component emits a custom event called Scrolled Index Change. Eventually, we'll use this to implement our infinite Scroll because it emits the index of the item that the user has scrolled to, so we can use that to determine when to pull the next batch of items from the database. Then you'll also notice that this component has its own public API that we can use to do things like measure the viewport or get the total item list size or even dynamically scroll to a specific element. In order to use those methods, you'll need to grab the component from the DOM. And you'll definitely want to strong type it, so we'll import the CDK Virtual Scroll Viewport component And then we can grab it with ViewChild. Then we can set it up as a property called Viewport. And if we start typing this out, you'll notice that it shows us a bunch of different methods that we can use. So that's how you can interact with the component dynamically in your TypeScript code. For example, if you have some chat messages, you could use this to scroll directly to bottom or update the scroll position when new messages are received. Now that you know the basics, we're ready to build out our Infinite Real Time Scroll using Firestore. I created an Infinite Scroll video with Firestore a while back, but today's solution will improve upon it in 2 big ways. First of all, it will make the list real time. So even though we're pulling items in batches, they'll still be updated in real time if the data changes on the back end. And the second obvious improvement is that this will be a virtual scroll, so we should be able to get much better performance as the user scrolls through more and more items. And also the CDK will just make our code much more simple and reliable. Now starting in the component TypeScript, we'll first import AngularFirestore, as well as a bunch of different stuff here from RxJS. We'll pull items from the database in batch sizes of 20. Then we'll set up a property for the viewport itself and then one to determine whether or not we've reached the end of the list. And then a behavior subject to keep track of the offset that we'll use to make our query to Firestore. And it's important that the subject starts with null and not undefined or an empty string because we need that null value to start our query to Firestore. So every time this offset changes, we're going to wanna make a new query to Firestore for the source data. But we only want to do that when the user scrolls to the bottom of the current list. So we can listen to that index change event that I showed you earlier And then we'll run this next batch method which takes the event as well as the very last item in the list as its argument. Then if we've reached the end, then we'll just go ahead and return and pull any additional data. But otherwise, we can take the Viewport rendered range end value and compare that to the total items in the actual list itself. And conveniently, the CDK has a method for that called Get Data Length. So if the ending index equals the total length of the items in the array, then we know that we've reached the end. We can send the next Offset value to our behavior subject, which will reactively pull a new batch of items from the database. And then while I have you here, I'm also going to set up a Track by function which just tracks the index of the items in the Array and only renders the ones that change. That's just a good thing to have for real time data because, otherwise, The entire list would rerender for each item that changes. So now I'm going to break this down into manageable chunks. The first one is getting an individual batch from the database. And to do that, we need to know the last seen item in the previous batch. I've added 200 records to Firestore of the same dummy data that I showed you earlier. And I want to do a paginated query on that data. So first I want to order by the user's name and then we'll start after the offset value, which needs to be null if we're starting a new batch. Otherwise, it needs to be the name of the last user and the last batch. And lastly, we'll limit our query to our batch size which again is 20. From there, we'll go ahead and call Snapshot Changes to get these Document Snapshots as an observable. If this observable emits an empty array, then it's safe to assume that we've reached the end of our collection in the database, at which point we can set the end property to true. Now the tricky part is keeping everything up to date in real time across multiple batches. In order to do that reliably, we need to take the array of documents and then map them to an object where the key is their document ID. D. This allows us to update a document's data anywhere in the list based on its key value instead of its index position. This ensures that Every document in the Array is unique because otherwise we are at risk of adding duplicate documents to the Array if things change between the Queries. So the next thing we wanna do is go into our constructor and set up a source observable that first listens to the offset value and then merges in new batches whenever the Offset changes, which again will happen when the user scrolls to the bottom of a list. That event can be fired multiple times, so I'm going to throttle it by 500 milliseconds just so we're not sending off redundant requests to the back end. Now you'll notice that I'm using Merge Map here to merge a new batch into the stream. It's important to use Merge Map here and not Switch Map because otherwise the previous subscriptions would be canceled, meaning that only the very last batch would be real time. From there, we can pipe in the Scan operator, which will just merge in the current batch with the existing batches. So this Batch Map Observable is our main source of truth because it contains all of the mappings for the individual item keys and their data. But we still want an actual array to loop over in the frontend. So we can go here and define our actual infinite array of objects as this batch map, but mapped to the object values which will give us an array of objects. And that takes care of the main logic for our Infinite Real Time Virtual Scroll. That may have seemed relatively complex, but in the grand scheme of things it's very simple considering what it does. Now we just have a couple of small modifications in the HTML. If you want to show an index in your list, you can just say let I equals index and then we'll also add in our trackBy function that we defined earlier. Then we can listen to that scrolledIndexChange event and the first argument will be the event itself, which is the index number. Then the second argument is the offset value for the query, which in our case will be the name of the very last person in the array. We can actually just parse that value right here in the template. Not the prettiest code in the world, but it works. Then the last thing we need to do is unwrap the infinite observable. My preferred way of doing this is using an NG Container and then unwrapping the Observable with the AsyncPipe. We can then say as people to give us a template variable of the array objects and then we can just use it throughout this HTML. Now if we pull up the demo, you can see that we have all of our objects here in the database And they're queried in the same alphabetical order that we get when we order them on the Firebase console. And if we make any changes to our data on the right, it will be reflected in the UI on the left in real time. It is important to keep in mind that the actual position of the item won't change, and if it's deleted, it won't be removed from the list. But if you need that functionality, that could definitely be made possible with some additional work in the code. Now, I'm pulling up the browser console, and you'll notice that there's an Array being console logged on the right every time we get to a certain event. The array size initially starts at 20, and then as we scroll down, it will We'll change the offset and our array grows from 20 to 40 to 60 and so on. And from the user's perspective, it happened so fast that Looks like this list existed all along, which is exactly what you want with an infinite scroll. But we are keeping track when we get to the end of the list, so when we finally reach that point we'll go ahead and give gives her some indication that there are no items left to retrieve. That's all folks. If this video helps you, please like and subscribe. And if you wanna take your development the next level, consider becoming a pro member at angularfirebase.com. You'll get access to all kinds of exclusive content like full courses and my book designed to help you build ship your app faster. Thanks for watching, and I'll talk to you soon.
Angular
angular
7
7
is
is
officially
officially
here
here
and
and
one
one
of
of
its
its
coolest
coolest
new
new
features
features
is
is
the
the
virtual
virtual
scroll
scroll
behavior
behavior
in
in
the
the
component
component
development
development
kit.
kit
It
it
allows
allows
you
you
to
to
easily
easily
build
build
a
a
high
high
performance
performance
list
list
that
that
only
only
renders
renders
the
the
items
items
visible
visible
to
to
the
the
user.
user
In
in
today's
today's
video,
video
I'll
i'll
show
show
you
you
everything
everything
you
you
need
need
to
to
know
know
about
about
virtual
virtual
scroll,
scroll
but
but
we'll
we'll
also
also
take
take
things
things
a
a
few
few
steps
steps
further
further
by
by
making
making
it
it
animated,
animated
infinite,
infinite
and
and
real
real
time.
time
If
if
you're
you're
new
new
here,
here
like
like
and
and
subscribe
subscribe
and
and
I'm
i'm
giving
giving
away
away
some
some
winter
winter
swag
swag
with
with
this
this
video.
video
All
all
you
you
have
have
to
to
do
do
is
is
leave
leave
a
a
comment
comment
below
below
and
and
we'll
we'll
pick
pick
a
a
winner
winner
on
on
the
the
live
live
stream
stream
on
on
Wednesday.
wednesday
So
so
first
first
of
of
all,
all
what
what
is
is
Virtual
virtual
scrolling
scrolling
and
and
why
why
would
would
you
you
want
want
to
to
use
use
it?
it
It's
it's
useful
useful
when
when
you
you
have
have
a
a
large
large
list
list
of
of
items
items
to
to
loop
loop
over,
over
but
but
you
you
only
only
want
want
to
to
render
render
the
the
items
items
that
that
are
are
visible
visible
in
in
the
the
viewport.
viewport
This
this
should
should
give
give
you
you
much
much
smoother
smoother
performance
performance
when
when
working
working
with
with
big
big
lists
lists
because
because
the
the
browser
browser
only
only
needs
needs
to
to
render
render
a
a
small
small
subset
subset
of
of
the
the
data.
data
The
the
CDK
cdk
provides
provides
a
a
handful
handful
of
of
components
components
and
and
directives
directives
to
to
make
make
virtual
virtual
scrolling
scrolling
super
super
easy.
easy
In
in
addition,
addition
it
it
provides
provides
a
a
handful
handful
of
of
tools
tools
that
that
makes
makes
it
it
much
much
easier
easier
to
to
measure
measure
the
the
scrolled
scrolled
element,
element
which
which
we
we
can
can
then
then
use
use
to
to
pull
pull
data
data
from
from
our
our
back
back
end
end
database
database
in
in
batches,
batches
giving
giving
us
us
an
an
infinite
infinite
real
real
time
time
virtual
virtual
scroll.
scroll
The
the
first
first
thing
thing
you'll
you'll
wanna
wanna
do
do
is
is
make
make
sure
sure
you're
you're
up
up
to
to
date
date
with
with
Angular
angular
version
version
7.
7
Then
then
we
we
can
can
quickly
quickly
add
add
the
the
CDK
cdk
to
to
our
our
app
app
by
by
running
running
ng
ng
add
add
angularmaterial.
angularmaterial
You'll
you'll
notice
notice
that
that
when
when
you
you
do
do
this
this
in
in
Angular
angular
7,
7
it
it
gives
gives
you
you
a
a
prompt
prompt
of
of
questions
questions
to
to
go
go
through.
through
You
you
can
can
just
just
select
select
the
the
defaults
defaults
and
and
then
then
if
if
you
you
also
also
want
want
to
to
follow
follow
along
along
with
with
the
the
Firebase
firebase
part,
part
you'll
you'll
want
want
to
to
install
install
AngularFire
angularfire
at
at
this
this
point
point
as
as
well.
well
But
but
I'm
i'm
going
going
to
to
start
start
with
with
just
just
the
the
CDK
cdk
basics
basics
before
before
we
we
get
get
to
to
that
that
point.
point
If
if
we
we
jump
jump
over
over
to
to
my
my
AppModule,
appmodule
you
you
can
can
see
see
that
that
I'm
i'm
importing
importing
the
the
scrolling
scrolling
module
module
from
from
the
the
CDK.
cdk
And
and
then
then
I
i
also
also
have
have
a
a
couple
couple
of
of
components
components
generated
generated
just
just
for
for
this
this
demo.
demo
So
so
you
you
wanna
wanna
make
make
sure
sure
that
that
you
you
have
have
the
the
scrolling
scrolling
module
module
here
here
in
in
the
the
import
import
section
section
and
and
then
then
optionally
optionally
AngularFire
angularfire
for
for
the
the
back
back
end
end
stuff.
stuff
Now
now
that
that
we're
we're
all
all
set
set
up,
up
let's
let's
start
start
by
by
looking
looking
at
at
the
the
basics
basics
of
of
Virtual
virtual
Scroll.
scroll
The
the
first
first
thing
thing
you'll
you'll
need
need
is
is
a
a
big
big
list
list
of
of
something
something
a
a
loop
loop
over,
over
so
so
I
i
wanna
wanna
show
show
you
you
how
how
to
to
generate
generate
a
a
ton
ton
of
of
fake
fake
data
data
really
really
quickly.
quickly
1st,
1st
I
i
wrote
wrote
my
my
own
own
helper
helper
function
function
that
that
will
will
return
return
a
a
random
random
emoji.
emoji
And
and
then
then
I
i
installed
installed
the
the
Faker
faker
JS
js
library
library
that
that
we
we
can
can
use
use
to
to
generate
generate
fake
fake
names
names
and
and
email
email
addresses
addresses
and
and
stuff
stuff
like
like
that.
that
Then
then
I'll
i'll
set
set
up
up
a
a
property
property
of
of
people,
people
and
and
then
then
I'll
i'll
define
define
that
that
property
property
as
as
Array
array
of
of
100
100
elements,
elements
then
then
we
we
can
can
map
map
each
each
element
element
in
in
that
that
Array
array
to
to
an
an
object
object
with
with
some
some
fake
fake
data.
data
So
so
we'll
we'll
give
give
each
each
object
object
a
a
name,
name
a
a
bio,
bio
and
and
an
an
emoji.
emoji
So
so
that
that
gives
gives
us
us
a
a
big
big
array
array
of
of
items
items
to
to
loop
loop
over.
over
Now
now
we
we
need
need
to
to
go
go
into
into
the
the
HTML
html
and
and
set
set
up
up
a
a
Context
context
for
for
our
our
virtual
virtual
scroll.
scroll
We
we
can
can
do
do
that
that
by
by
just
just
dropping
dropping
in
in
the
the
CDK
cdk
Virtual
virtual
Scroll
scroll
Viewport
viewport
component.
component
It
it
has
has
one
one
required
required
argument
argument
which
which
is
is
the
the
item
item
size
size
or
or
height
height
which
which
is
is
represented
represented
in
in
pixels.
pixels
So
so
typically
typically
with
with
a
a
virtual
virtual
scroll,
scroll
you
you
want
want
items
items
to
to
have
have
Fixed
fixed
height
height
if
if
possible.
possible
So
so
with
with
that
that
being
being
said,
said
let's
let's
quickly
quickly
jump
jump
over
over
to
to
our
our
CSS
css
code.
code
You'll
you'll
first
first
wanna
wanna
make
make
sure
sure
that
that
your
your
Viewport
viewport
has
has
some
some
height.
height
So
so
in
in
this
this
case,
case
I'm
i'm
having
having
it
it
fill
fill
up
up
the
the
entire
entire
height
height
of
of
the
the
Viewport.
viewport
Then
then
each
each
item
item
inside
inside
of
of
my
my
Viewport
viewport
will
will
have
have
a
a
fixed
fixed
height
height
of
of
100
100
pixels,
pixels
which
which
matches
matches
the
the
item
item
size
size
argument
argument
in
in
the
the
component
component
itself.
itself
And
and
just
just
for
for
bonus
bonus
points,
points
if
if
you
you
want
want
to
to
make
make
it
it
look
look
cool,
cool
this
this
is
is
a
a
good
good
place
place
to
to
customize
customize
the
the
pseudo
pseudo
selector
selector
for
for
the
the
actual
actual
ScrollBar
scrollbar
element
element
to
to
give
give
it
it
a
a
different
different
style
style
and
and
size
size
as
as
the
the
user
user
scrolls
scrolls
down.
down
When
when
inside
inside
the
the
context
context
of
of
a
a
ScrollViewport,
scrollviewport
The
the
CDK
cdk
gives
gives
you
you
a
a
structural
structural
directive
directive
called
called
CDK
cdk
virtual
virtual
4.
4
It
it
replaces
replaces
ng
ng
4,
4
but
but
it
it
works
works
exactly
exactly
the
the
same
same
way.
way
If
if
we
we
go
go
ahead
ahead
and
and
serve
serve
the
the
app,
app
what's
what's
happening
happening
is
is
that
that
the
the
CDK
cdk
is
is
keeping
keeping
track
track
of
of
the
the
items
items
in
in
the
the
list
list
and
and
which
which
items
items
will
will
fit
fit
into
into
the
the
viewport.
viewport
A
a
good
good
way
way
to
to
demonstrate
demonstrate
this
this
is
is
just
just
by
by
using
using
a
a
plain
plain
CSS
css
animation
animation
because
because
that
that
animation
animation
will
will
be
be
applied
applied
each
each
time
time
the
the
item
item
is
is
re
re
rendered.
rendered
So
so
I'm
i'm
using
using
a
a
little
little
library
library
here
here
you
you
may
may
have
have
heard
heard
of
of
called
called
Animate
animate
CSS.
css
And
and
you'll
you'll
notice
notice
as
as
I
i
scroll
scroll
up
up
and
and
down,
down
the
the
animation
animation
is
is
reapplied
reapplied
to
to
each
each
element
element
because
because
it's
it's
being
being
re
re
rendered
rendered
in
in
the
the
DOM.
dom
In
in
a
a
regular
regular
list,
list
the
the
CSS
css
animation
animation
would
would
only
only
be
be
applied
applied
on
on
the
the
initial
initial
page
page
load.
load
So
so
that's
that's
kind
kind
of
of
an
an
interesting
interesting
little
little
side
side
effect
effect
with
with
VirtualScroll
virtualscroll
that
that
you
you
can
can
play
play
with.
with
Now
now
there's
there's
a
a
couple
couple
of
of
other
other
basic
basic
things
things
that
that
you
you
need
need
to
to
know.
know
First
first
of
of
all,
all
the
the
virtual
virtual
scroll
scroll
component
component
emits
emits
a
a
custom
custom
event
event
called
called
Scrolled
scrolled
Index
index
Change.
change
Eventually,
eventually
we'll
we'll
use
use
this
this
to
to
implement
implement
our
our
infinite
infinite
Scroll
scroll
because
because
it
it
emits
emits
the
the
index
index
of
of
the
the
item
item
that
that
the
the
user
user
has
has
scrolled
scrolled
to,
to
so
so
we
we
can
can
use
use
that
that
to
to
determine
determine
when
when
to
to
pull
pull
the
the
next
next
batch
batch
of
of
items
items
from
from
the
the
database.
database
Then
then
you'll
you'll
also
also
notice
notice
that
that
this
this
component
component
has
has
its
its
own
own
public
public
API
api
that
that
we
we
can
can
use
use
to
to
do
do
things
things
like
like
measure
measure
the
the
viewport
viewport
or
or
get
get
the
the
total
total
item
item
list
list
size
size
or
or
even
even
dynamically
dynamically
scroll
scroll
to
to
a
a
specific
specific
element.
element
In
in
order
order
to
to
use
use
those
those
methods,
methods
you'll
you'll
need
need
to
to
grab
grab
the
the
component
component
from
from
the
the
DOM.
dom
And
and
you'll
you'll
definitely
definitely
want
want
to
to
strong
strong
type
type
it,
it
so
so
we'll
we'll
import
import
the
the
CDK
cdk
Virtual
virtual
Scroll
scroll
Viewport
viewport
component
component
And
and
then
then
we
we
can
can
grab
grab
it
it
with
with
ViewChild.
viewchild
Then
then
we
we
can
can
set
set
it
it
up
up
as
as
a
a
property
property
called
called
Viewport.
viewport
And
and
if
if
we
we
start
start
typing
typing
this
this
out,
out
you'll
you'll
notice
notice
that
that
it
it
shows
shows
us
us
a
a
bunch
bunch
of
of
different
different
methods
methods
that
that
we
we
can
can
use.
use
So
so
that's
that's
how
how
you
you
can
can
interact
interact
with
with
the
the
component
component
dynamically
dynamically
in
in
your
your
TypeScript
typescript
code.
code
For
for
example,
example
if
if
you
you
have
have
some
some
chat
chat
messages,
messages
you
you
could
could
use
use
this
this
to
to
scroll
scroll
directly
directly
to
to
bottom
bottom
or
or
update
update
the
the
scroll
scroll
position
position
when
when
new
new
messages
messages
are
are
received.
received
Now
now
that
that
you
you
know
know
the
the
basics,
basics
we're
we're
ready
ready
to
to
build
build
out
out
our
our
Infinite
infinite
Real
real
Time
time
Scroll
scroll
using
using
Firestore.
firestore
I
i
created
created
an
an
Infinite
infinite
Scroll
scroll
video
video
with
with
Firestore
firestore
a
a
while
while
back,
back
but
but
today's
today's
solution
solution
will
will
improve
improve
upon
upon
it
it
in
in
2
2
big
big
ways.
ways
First
first
of
of
all,
all
it
it
will
will
make
make
the
the
list
list
real
real
time.
time
So
so
even
even
though
though
we're
we're
pulling
pulling
items
items
in
in
batches,
batches
they'll
they'll
still
still
be
be
updated
updated
in
in
real
real
time
time
if
if
the
the
data
data
changes
changes
on
on
the
the
back
back
end.
end
And
and
the
the
second
second
obvious
obvious
improvement
improvement
is
is
that
that
this
this
will
will
be
be
a
a
virtual
virtual
scroll,
scroll
so
so
we
we
should
should
be
be
able
able
to
to
get
get
much
much
better
better
performance
performance
as
as
the
the
user
user
scrolls
scrolls
through
through
more
more
and
and
more
more
items.
items
And
and
also
also
the
the
CDK
cdk
will
will
just
just
make
make
our
our
code
code
much
much
more
more
simple
simple
and
and
reliable.
reliable
Now
now
starting
starting
in
in
the
the
component
component
TypeScript,
typescript
we'll
we'll
first
first
import
import
AngularFirestore,
angularfirestore
as
as
well
well
as
as
a
a
bunch
bunch
of
of
different
different
stuff
stuff
here
here
from
from
RxJS.
rxjs
We'll
we'll
pull
pull
items
items
from
from
the
the
database
database
in
in
batch
batch
sizes
sizes
of
of
20.
20
Then
then
we'll
we'll
set
set
up
up
a
a
property
property
for
for
the
the
viewport
viewport
itself
itself
and
and
then
then
one
one
to
to
determine
determine
whether
whether
or
or
not
not
we've
we've
reached
reached
the
the
end
end
of
of
the
the
list.
list
And
and
then
then
a
a
behavior
behavior
subject
subject
to
to
keep
keep
track
track
of
of
the
the
offset
offset
that
that
we'll
we'll
use
use
to
to
make
make
our
our
query
query
to
to
Firestore.
firestore
And
and
it's
it's
important
important
that
that
the
the
subject
subject
starts
starts
with
with
null
null
and
and
not
not
undefined
undefined
or
or
an
an
empty
empty
string
string
because
because
we
we
need
need
that
that
null
null
value
value
to
to
start
start
our
our
query
query
to
to
Firestore.
firestore
So
so
every
every
time
time
this
this
offset
offset
changes,
changes
we're
we're
going
going
to
to
wanna
wanna
make
make
a
a
new
new
query
query
to
to
Firestore
firestore
for
for
the
the
source
source
data.
data
But
but
we
we
only
only
want
want
to
to
do
do
that
that
when
when
the
the
user
user
scrolls
scrolls
to
to
the
the
bottom
bottom
of
of
the
the
current
current
list.
list
So
so
we
we
can
can
listen
listen
to
to
that
that
index
index
change
change
event
event
that
that
I
i
showed
showed
you
you
earlier
earlier
And
and
then
then
we'll
we'll
run
run
this
this
next
next
batch
batch
method
method
which
which
takes
takes
the
the
event
event
as
as
well
well
as
as
the
the
very
very
last
last
item
item
in
in
the
the
list
list
as
as
its
its
argument.
argument
Then
then
if
if
we've
we've
reached
reached
the
the
end,
end
then
then
we'll
we'll
just
just
go
go
ahead
ahead
and
and
return
return
and
and
pull
pull
any
any
additional
additional
data.
data
But
but
otherwise,
otherwise
we
we
can
can
take
take
the
the
Viewport
viewport
rendered
rendered
range
range
end
end
value
value
and
and
compare
compare
that
that
to
to
the
the
total
total
items
items
in
in
the
the
actual
actual
list
list
itself.
itself
And
and
conveniently,
conveniently
the
the
CDK
cdk
has
has
a
a
method
method
for
for
that
that
called
called
Get
get
Data
data
Length.
length
So
so
if
if
the
the
ending
ending
index
index
equals
equals
the
the
total
total
length
length
of
of
the
the
items
items
in
in
the
the
array,
array
then
then
we
we
know
know
that
that
we've
we've
reached
reached
the
the
end.
end
We
we
can
can
send
send
the
the
next
next
Offset
offset
value
value
to
to
our
our
behavior
behavior
subject,
subject
which
which
will
will
reactively
reactively
pull
pull
a
a
new
new
batch
batch
of
of
items
items
from
from
the
the
database.
database
And
and
then
then
while
while
I
i
have
have
you
you
here,
here
I'm
i'm
also
also
going
going
to
to
set
set
up
up
a
a
Track
track
by
by
function
function
which
which
just
just
tracks
tracks
the
the
index
index
of
of
the
the
items
items
in
in
the
the
Array
array
and
and
only
only
renders
renders
the
the
ones
ones
that
that
change.
change
That's
that's
just
just
a
a
good
good
thing
thing
to
to
have
have
for
for
real
real
time
time
data
data
because,
because
otherwise,
otherwise
The
the
entire
entire
list
list
would
would
rerender
rerender
for
for
each
each
item
item
that
that
changes.
changes
So
so
now
now
I'm
i'm
going
going
to
to
break
break
this
this
down
down
into
into
manageable
manageable
chunks.
chunks
The
the
first
first
one
one
is
is
getting
getting
an
an
individual
individual
batch
batch
from
from
the
the
database.
database
And
and
to
to
do
do
that,
that
we
we
need
need
to
to
know
know
the
the
last
last
seen
seen
item
item
in
in
the
the
previous
previous
batch.
batch
I've
i've
added
added
200
200
records
records
to
to
Firestore
firestore
of
of
the
the
same
same
dummy
dummy
data
data
that
that
I
i
showed
showed
you
you
earlier.
earlier
And
and
I
i
want
want
to
to
do
do
a
a
paginated
paginated
query
query
on
on
that
that
data.
data
So
so
first
first
I
i
want
want
to
to
order
order
by
by
the
the
user's
user's
name
name
and
and
then
then
we'll
we'll
start
start
after
after
the
the
offset
offset
value,
value
which
which
needs
needs
to
to
be
be
null
null
if
if
we're
we're
starting
starting
a
a
new
new
batch.
batch
Otherwise,
otherwise
it
it
needs
needs
to
to
be
be
the
the
name
name
of
of
the
the
last
last
user
user
and
and
the
the
last
last
batch.
batch
And
and
lastly,
lastly
we'll
we'll
limit
limit
our
our
query
query
to
to
our
our
batch
batch
size
size
which
which
again
again
is
is
20.
20
From
from
there,
there
we'll
we'll
go
go
ahead
ahead
and
and
call
call
Snapshot
snapshot
Changes
changes
to
to
get
get
these
these
Document
document
Snapshots
snapshots
as
as
an
an
observable.
observable
If
if
this
this
observable
observable
emits
emits
an
an
empty
empty
array,
array
then
then
it's
it's
safe
safe
to
to
assume
assume
that
that
we've
we've
reached
reached
the
the
end
end
of
of
our
our
collection
collection
in
in
the
the
database,
database
at
at
which
which
point
point
we
we
can
can
set
set
the
the
end
end
property
property
to
to
true.
true
Now
now
the
the
tricky
tricky
part
part
is
is
keeping
keeping
everything
everything
up
up
to
to
date
date
in
in
real
real
time
time
across
across
multiple
multiple
batches.
batches
In
in
order
order
to
to
do
do
that
that
reliably,
reliably
we
we
need
need
to
to
take
take
the
the
array
array
of
of
documents
documents
and
and
then
then
map
map
them
them
to
to
an
an
object
object
where
where
the
the
key
key
is
is
their
their
document
document
ID.
id
D.
d
This
this
allows
allows
us
us
to
to
update
update
a
a
document's
document's
data
data
anywhere
anywhere
in
in
the
the
list
list
based
based
on
on
its
its
key
key
value
value
instead
instead
of
of
its
its
index
index
position.
position
This
this
ensures
ensures
that
that
Every
every
document
document
in
in
the
the
Array
array
is
is
unique
unique
because
because
otherwise
otherwise
we
we
are
are
at
at
risk
risk
of
of
adding
adding
duplicate
duplicate
documents
documents
to
to
the
the
Array
array
if
if
things
things
change
change
between
between
the
the
Queries.
queries
So
so
the
the
next
next
thing
thing
we
we
wanna
wanna
do
do
is
is
go
go
into
into
our
our
constructor
constructor
and
and
set
set
up
up
a
a
source
source
observable
observable
that
that
first
first
listens
listens
to
to
the
the
offset
offset
value
value
and
and
then
then
merges
merges
in
in
new
new
batches
batches
whenever
whenever
the
the
Offset
offset
changes,
changes
which
which
again
again
will
will
happen
happen
when
when
the
the
user
user
scrolls
scrolls
to
to
the
the
bottom
bottom
of
of
a
a
list.
list
That
that
event
event
can
can
be
be
fired
fired
multiple
multiple
times,
times
so
so
I'm
i'm
going
going
to
to
throttle
throttle
it
it
by
by
500
500
milliseconds
milliseconds
just
just
so
so
we're
we're
not
not
sending
sending
off
off
redundant
redundant
requests
requests
to
to
the
the
back
back
end.
end
Now
now
you'll
you'll
notice
notice
that
that
I'm
i'm
using
using
Merge
merge
Map
map
here
here
to
to
merge
merge
a
a
new
new
batch
batch
into
into
the
the
stream.
stream
It's
it's
important
important
to
to
use
use
Merge
merge
Map
map
here
here
and
and
not
not
Switch
switch
Map
map
because
because
otherwise
otherwise
the
the
previous
previous
subscriptions
subscriptions
would
would
be
be
canceled,
canceled
meaning
meaning
that
that
only
only
the
the
very
very
last
last
batch
batch
would
would
be
be
real
real
time.
time
From
from
there,
there
we
we
can
can
pipe
pipe
in
in
the
the
Scan
scan
operator,
operator
which
which
will
will
just
just
merge
merge
in
in
the
the
current
current
batch
batch
with
with
the
the
existing
existing
batches.
batches
So
so
this
this
Batch
batch
Map
map
Observable
observable
is
is
our
our
main
main
source
source
of
of
truth
truth
because
because
it
it
contains
contains
all
all
of
of
the
the
mappings
mappings
for
for
the
the
individual
individual
item
item
keys
keys
and
and
their
their
data.
data
But
but
we
we
still
still
want
want
an
an
actual
actual
array
array
to
to
loop
loop
over
over
in
in
the
the
frontend.
frontend
So
so
we
we
can
can
go
go
here
here
and
and
define
define
our
our
actual
actual
infinite
infinite
array
array
of
of
objects
objects
as
as
this
this
batch
batch
map,
map
but
but
mapped
mapped
to
to
the
the
object
object
values
values
which
which
will
will
give
give
us
us
an
an
array
array
of
of
objects.
objects
And
and
that
that
takes
takes
care
care
of
of
the
the
main
main
logic
logic
for
for
our
our
Infinite
infinite
Real
real
Time
time
Virtual
virtual
Scroll.
scroll
That
that
may
may
have
have
seemed
seemed
relatively
relatively
complex,
complex
but
but
in
in
the
the
grand
grand
scheme
scheme
of
of
things
things
it's
it's
very
very
simple
simple
considering
considering
what
what
it
it
does.
does
Now
now
we
we
just
just
have
have
a
a
couple
couple
of
of
small
small
modifications
modifications
in
in
the
the
HTML.
html
If
if
you
you
want
want
to
to
show
show
an
an
index
index
in
in
your
your
list,
list
you
you
can
can
just
just
say
say
let
let
I
i
equals
equals
index
index
and
and
then
then
we'll
we'll
also
also
add
add
in
in
our
our
trackBy
trackby
function
function
that
that
we
we
defined
defined
earlier.
earlier
Then
then
we
we
can
can
listen
listen
to
to
that
that
scrolledIndexChange
scrolledindexchange
event
event
and
and
the
the
first
first
argument
argument
will
will
be
be
the
the
event
event
itself,
itself
which
which
is
is
the
the
index
index
number.
number
Then
then
the
the
second
second
argument
argument
is
is
the
the
offset
offset
value
value
for
for
the
the
query,
query
which
which
in
in
our
our
case
case
will
will
be
be
the
the
name
name
of
of
the
the
very
very
last
last
person
person
in
in
the
the
array.
array
We
we
can
can
actually
actually
just
just
parse
parse
that
that
value
value
right
right
here
here
in
in
the
the
template.
template
Not
not
the
the
prettiest
prettiest
code
code
in
in
the
the
world,
world
but
but
it
it
works.
works
Then
then
the
the
last
last
thing
thing
we
we
need
need
to
to
do
do
is
is
unwrap
unwrap
the
the
infinite
infinite
observable.
observable
My
my
preferred
preferred
way
way
of
of
doing
doing
this
this
is
is
using
using
an
an
NG
ng
Container
container
and
and
then
then
unwrapping
unwrapping
the
the
Observable
observable
with
with
the
the
AsyncPipe.
asyncpipe
We
we
can
can
then
then
say
say
as
as
people
people
to
to
give
give
us
us
a
a
template
template
variable
variable
of
of
the
the
array
array
objects
objects
and
and
then
then
we
we
can
can
just
just
use
use
it
it
throughout
throughout
this
this
HTML.
html
Now
now
if
if
we
we
pull
pull
up
up
the
the
demo,
demo
you
you
can
can
see
see
that
that
we
we
have
have
all
all
of
of
our
our
objects
objects
here
here
in
in
the
the
database
database
And
and
they're
they're
queried
queried
in
in
the
the
same
same
alphabetical
alphabetical
order
order
that
that
we
we
get
get
when
when
we
we
order
order
them
them
on
on
the
the
Firebase
firebase
console.
console
And
and
if
if
we
we
make
make
any
any
changes
changes
to
to
our
our
data
data
on
on
the
the
right,
right
it
it
will
will
be
be
reflected
reflected
in
in
the
the
UI
ui
on
on
the
the
left
left
in
in
real
real
time.
time
It
it
is
is
important
important
to
to
keep
keep
in
in
mind
mind
that
that
the
the
actual
actual
position
position
of
of
the
the
item
item
won't
won't
change,
change
and
and
if
if
it's
it's
deleted,
deleted
it
it
won't
won't
be
be
removed
removed
from
from
the
the
list.
list
But
but
if
if
you
you
need
need
that
that
functionality,
functionality
that
that
could
could
definitely
definitely
be
be
made
made
possible
possible
with
with
some
some
additional
additional
work
work
in
in
the
the
code.
code
Now,
now
I'm
i'm
pulling
pulling
up
up
the
the
browser
browser
console,
console
and
and
you'll
you'll
notice
notice
that
that
there's
there's
an
an
Array
array
being
being
console
console
logged
logged
on
on
the
the
right
right
every
every
time
time
we
we
get
get
to
to
a
a
certain
certain
event.
event
The
the
array
array
size
size
initially
initially
starts
starts
at
at
20,
20
and
and
then
then
as
as
we
we
scroll
scroll
down,
down
it
it
will
will
We'll
we'll
change
change
the
the
offset
offset
and
and
our
our
array
array
grows
grows
from
from
20
20
to
to
40
40
to
to
60
60
and
and
so
so
on.
on
And
and
from
from
the
the
user's
user's
perspective,
perspective
it
it
happened
happened
so
so
fast
fast
that
that
Looks
looks
like
like
this
this
list
list
existed
existed
all
all
along,
along
which
which
is
is
exactly
exactly
what
what
you
you
want
want
with
with
an
an
infinite
infinite
scroll.
scroll
But
but
we
we
are
are
keeping
keeping
track
track
when
when
we
we
get
get
to
to
the
the
end
end
of
of
the
the
list,
list
so
so
when
when
we
we
finally
finally
reach
reach
that
that
point
point
we'll
we'll
go
go
ahead
ahead
and
and
give
give
gives
gives
her
her
some
some
indication
indication
that
that
there
there
are
are
no
no
items
items
left
left
to
to
retrieve.
retrieve
That's
that's
all
all
folks.
folks
If
if
this
this
video
video
helps
helps
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you
you
wanna
wanna
take
take
your
your
development
development
the
the
next
next
level,
level
consider
consider
becoming
becoming
a
a
pro
pro
member
member
at
at
angularfirebase.com.
angularfirebase.com
You'll
you'll
get
get
access
access
to
to
all
all
kinds
kinds
of
of
exclusive
exclusive
content
content
like
like
full
full
courses
courses
and
and
my
my
book
book
designed
designed
to
to
help
help
you
you
build
build
ship
ship
your
your
app
app
faster.
faster
Thanks
thanks
for
for
watching,
watching
and
and
I'll
i'll
talk
talk
to
to
you
you
soon.
soon
success
The virtual scroll in component development kit is discussed, including the benefits of high-performance lists and how to use it in code development. The process for creating a list and updating data structures is also discussed, including customizing alignment and importing from Fir tired. A source-only method is recommended to avoid unnecessary queries and merging objects and updating data structures. A demo is also provided, and viewers are encouraged to like and subscribe to their channel.
Angular 7 Virtual Scroll - To Infinity and Beyond
Angular 7 is officially here and one of its coolest new features is the virtual scroll behavior in the component development kit. It allows you to easily build a high performance list that only renders the items visible to the user. In today's video, I'll show you everything you need to know about virtual scroll, but we'll also take things a few steps further by making it animated, infinite, and real time. If you're new here, like and subscribe and I'm giving away some winter swag with this video. All you have to do is leave a comment below and we'll pick a winner on the live stream on Wednesday. So first of all, what is Virtual scrolling and why would you want to use it? It's useful when you have a large list of items to loop over, but you only want to render the items that are visible in the viewport. This should give you much smoother performance when working with big lists because the browser only needs to render a small subset of the data. The CDK provides a handful of components and directives to make virtual scrolling super easy. In addition, it provides a handful of tools that makes it much easier to measure the scrolled element, which we can then use to pull data from our back end database in batches, giving us an infinite real time virtual scroll. The first thing you'll wanna do is make sure you're up to date with Angular version 7. Then we can quickly add the CDK to our app by running ng add angularmaterial. You'll notice that when you do this in Angular 7, it gives you a prompt of questions to go through. You can just select the defaults and then if you also want to follow along with the Firebase part, you'll want to install AngularFire at this point as well. But I'm going to start with just the CDK basics before we get to that point. If we jump over to my AppModule, you can see that I'm importing the scrolling module from the CDK. And then I also have a couple of components generated just for this demo. So you wanna make sure that you have the scrolling module here in the import section and then optionally AngularFire for the back end stuff. Now that we're all set up, let's start by looking at the basics of Virtual Scroll. The first thing you'll need is a big list of something a loop over, so I wanna show you how to generate a ton of fake data really quickly. 1st, I wrote my own helper function that will return a random emoji. And then I installed the Faker JS library that we can use to generate fake names and email addresses and stuff like that. Then I'll set up a property of people, and then I'll define that property as Array of 100 elements, then we can map each element in that Array to an object with some fake data. So we'll give each object a name, a bio, and an emoji. So that gives us a big array of items to loop over. Now we need to go into the HTML and set up a Context for our virtual scroll. We can do that by just dropping in the CDK Virtual Scroll Viewport component. It has one required argument which is the item size or height which is represented in pixels. So typically with a virtual scroll, you want items to have Fixed height if possible. So with that being said, let's quickly jump over to our CSS code. You'll first wanna make sure that your Viewport has some height. So in this case, I'm having it fill up the entire height of the Viewport. Then each item inside of my Viewport will have a fixed height of 100 pixels, which matches the item size argument in the component itself. And just for bonus points, if you want to make it look cool, this is a good place to customize the pseudo selector for the actual ScrollBar element to give it a different style and size as the user scrolls down. When inside the context of a ScrollViewport, The CDK gives you a structural directive called CDK virtual 4. It replaces ng 4, but it works exactly the same way. If we go ahead and serve the app, what's happening is that the CDK is keeping track of the items in the list and which items will fit into the viewport. A good way to demonstrate this is just by using a plain CSS animation because that animation will be applied each time the item is re rendered. So I'm using a little library here you may have heard of called Animate CSS. And you'll notice as I scroll up and down, the animation is reapplied to each element because it's being re rendered in the DOM. In a regular list, the CSS animation would only be applied on the initial page load. So that's kind of an interesting little side effect with VirtualScroll that you can play with. Now there's a couple of other basic things that you need to know. First of all, the virtual scroll component emits a custom event called Scrolled Index Change. Eventually, we'll use this to implement our infinite Scroll because it emits the index of the item that the user has scrolled to, so we can use that to determine when to pull the next batch of items from the database. Then you'll also notice that this component has its own public API that we can use to do things like measure the viewport or get the total item list size or even dynamically scroll to a specific element. In order to use those methods, you'll need to grab the component from the DOM. And you'll definitely want to strong type it, so we'll import the CDK Virtual Scroll Viewport component And then we can grab it with ViewChild. Then we can set it up as a property called Viewport. And if we start typing this out, you'll notice that it shows us a bunch of different methods that we can use. So that's how you can interact with the component dynamically in your TypeScript code. For example, if you have some chat messages, you could use this to scroll directly to bottom or update the scroll position when new messages are received. Now that you know the basics, we're ready to build out our Infinite Real Time Scroll using Firestore. I created an Infinite Scroll video with Firestore a while back, but today's solution will improve upon it in 2 big ways. First of all, it will make the list real time. So even though we're pulling items in batches, they'll still be updated in real time if the data changes on the back end. And the second obvious improvement is that this will be a virtual scroll, so we should be able to get much better performance as the user scrolls through more and more items. And also the CDK will just make our code much more simple and reliable. Now starting in the component TypeScript, we'll first import AngularFirestore, as well as a bunch of different stuff here from RxJS. We'll pull items from the database in batch sizes of 20. Then we'll set up a property for the viewport itself and then one to determine whether or not we've reached the end of the list. And then a behavior subject to keep track of the offset that we'll use to make our query to Firestore. And it's important that the subject starts with null and not undefined or an empty string because we need that null value to start our query to Firestore. So every time this offset changes, we're going to wanna make a new query to Firestore for the source data. But we only want to do that when the user scrolls to the bottom of the current list. So we can listen to that index change event that I showed you earlier And then we'll run this next batch method which takes the event as well as the very last item in the list as its argument. Then if we've reached the end, then we'll just go ahead and return and pull any additional data. But otherwise, we can take the Viewport rendered range end value and compare that to the total items in the actual list itself. And conveniently, the CDK has a method for that called Get Data Length. So if the ending index equals the total length of the items in the array, then we know that we've reached the end. We can send the next Offset value to our behavior subject, which will reactively pull a new batch of items from the database. And then while I have you here, I'm also going to set up a Track by function which just tracks the index of the items in the Array and only renders the ones that change. That's just a good thing to have for real time data because, otherwise, The entire list would rerender for each item that changes. So now I'm going to break this down into manageable chunks. The first one is getting an individual batch from the database. And to do that, we need to know the last seen item in the previous batch. I've added 200 records to Firestore of the same dummy data that I showed you earlier. And I want to do a paginated query on that data. So first I want to order by the user's name and then we'll start after the offset value, which needs to be null if we're starting a new batch. Otherwise, it needs to be the name of the last user and the last batch. And lastly, we'll limit our query to our batch size which again is 20. From there, we'll go ahead and call Snapshot Changes to get these Document Snapshots as an observable. If this observable emits an empty array, then it's safe to assume that we've reached the end of our collection in the database, at which point we can set the end property to true. Now the tricky part is keeping everything up to date in real time across multiple batches. In order to do that reliably, we need to take the array of documents and then map them to an object where the key is their document ID. D. This allows us to update a document's data anywhere in the list based on its key value instead of its index position. This ensures that Every document in the Array is unique because otherwise we are at risk of adding duplicate documents to the Array if things change between the Queries. So the next thing we wanna do is go into our constructor and set up a source observable that first listens to the offset value and then merges in new batches whenever the Offset changes, which again will happen when the user scrolls to the bottom of a list. That event can be fired multiple times, so I'm going to throttle it by 500 milliseconds just so we're not sending off redundant requests to the back end. Now you'll notice that I'm using Merge Map here to merge a new batch into the stream. It's important to use Merge Map here and not Switch Map because otherwise the previous subscriptions would be canceled, meaning that only the very last batch would be real time. From there, we can pipe in the Scan operator, which will just merge in the current batch with the existing batches. So this Batch Map Observable is our main source of truth because it contains all of the mappings for the individual item keys and their data. But we still want an actual array to loop over in the frontend. So we can go here and define our actual infinite array of objects as this batch map, but mapped to the object values which will give us an array of objects. And that takes care of the main logic for our Infinite Real Time Virtual Scroll. That may have seemed relatively complex, but in the grand scheme of things it's very simple considering what it does. Now we just have a couple of small modifications in the HTML. If you want to show an index in your list, you can just say let I equals index and then we'll also add in our trackBy function that we defined earlier. Then we can listen to that scrolledIndexChange event and the first argument will be the event itself, which is the index number. Then the second argument is the offset value for the query, which in our case will be the name of the very last person in the array. We can actually just parse that value right here in the template. Not the prettiest code in the world, but it works. Then the last thing we need to do is unwrap the infinite observable. My preferred way of doing this is using an NG Container and then unwrapping the Observable with the AsyncPipe. We can then say as people to give us a template variable of the array objects and then we can just use it throughout this HTML. Now if we pull up the demo, you can see that we have all of our objects here in the database And they're queried in the same alphabetical order that we get when we order them on the Firebase console. And if we make any changes to our data on the right, it will be reflected in the UI on the left in real time. It is important to keep in mind that the actual position of the item won't change, and if it's deleted, it won't be removed from the list. But if you need that functionality, that could definitely be made possible with some additional work in the code. Now, I'm pulling up the browser console, and you'll notice that there's an Array being console logged on the right every time we get to a certain event. The array size initially starts at 20, and then as we scroll down, it will We'll change the offset and our array grows from 20 to 40 to 60 and so on. And from the user's perspective, it happened so fast that Looks like this list existed all along, which is exactly what you want with an infinite scroll. But we are keeping track when we get to the end of the list, so when we finally reach that point we'll go ahead and give gives her some indication that there are no items left to retrieve. That's all folks. If this video helps you, please like and subscribe. And if you wanna take your development the next level, consider becoming a pro member at angularfirebase.com. You'll get access to all kinds of exclusive content like full courses and my book designed to help you build ship your app faster. Thanks for watching, and I'll talk to you soon.
The virtual scroll in component development kit is discussed, including the benefits of high-performance lists and how to use it in code development. The process for creating a list and updating data structures is also discussed, including customizing alignment and importing from Fir tired. A source-only method is recommended to avoid unnecessary queries and merging objects and updating data structures. A demo is also provided, and viewers are encouraged to like and subscribe to their channel.

        [INST]
        You are youtuber called Fireship you make engaging high-intensity and entertaining coding tutorials and tech news. 
        you covers a wide range of topics relevant to programmers, aiming to help them learn and improve their skills quickly.
        
        Given the title of the video : Angular 7 Virtual Scroll - To Infinity and Beyond 
        and a small summary : The virtual scroll in component development kit is discussed, including the benefits of high-performance lists and how to use it in code development. The process for creating a list and updating data structures is also discussed, including customizing alignment and importing from Fir tired. A source-only method is recommended to avoid unnecessary queries and merging objects and updating data structures. A demo is also provided, and viewers are encouraged to like and subscribe to their channel.
        [/INST]
        
        Generate the video : Angular 7 is officially here and one of its coolest new features is the virtual scroll behavior in the component development kit. It allows you to easily build a high performance list that only renders the items visible to the user. In today's video, I'll show you everything you need to know about virtual scroll, but we'll also take things a few steps further by making it animated, infinite, and real time. If you're new here, like and subscribe and I'm giving away some winter swag with this video. All you have to do is leave a comment below and we'll pick a winner on the live stream on Wednesday. So first of all, what is Virtual scrolling and why would you want to use it? It's useful when you have a large list of items to loop over, but you only want to render the items that are visible in the viewport. This should give you much smoother performance when working with big lists because the browser only needs to render a small subset of the data. The CDK provides a handful of components and directives to make virtual scrolling super easy. In addition, it provides a handful of tools that makes it much easier to measure the scrolled element, which we can then use to pull data from our back end database in batches, giving us an infinite real time virtual scroll. The first thing you'll wanna do is make sure you're up to date with Angular version 7. Then we can quickly add the CDK to our app by running ng add angularmaterial. You'll notice that when you do this in Angular 7, it gives you a prompt of questions to go through. You can just select the defaults and then if you also want to follow along with the Firebase part, you'll want to install AngularFire at this point as well. But I'm going to start with just the CDK basics before we get to that point. If we jump over to my AppModule, you can see that I'm importing the scrolling module from the CDK. And then I also have a couple of components generated just for this demo. So you wanna make sure that you have the scrolling module here in the import section and then optionally AngularFire for the back end stuff. Now that we're all set up, let's start by looking at the basics of Virtual Scroll. The first thing you'll need is a big list of something a loop over, so I wanna show you how to generate a ton of fake data really quickly. 1st, I wrote my own helper function that will return a random emoji. And then I installed the Faker JS library that we can use to generate fake names and email addresses and stuff like that. Then I'll set up a property of people, and then I'll define that property as Array of 100 elements, then we can map each element in that Array to an object with some fake data. So we'll give each object a name, a bio, and an emoji. So that gives us a big array of items to loop over. Now we need to go into the HTML and set up a Context for our virtual scroll. We can do that by just dropping in the CDK Virtual Scroll Viewport component. It has one required argument which is the item size or height which is represented in pixels. So typically with a virtual scroll, you want items to have Fixed height if possible. So with that being said, let's quickly jump over to our CSS code. You'll first wanna make sure that your Viewport has some height. So in this case, I'm having it fill up the entire height of the Viewport. Then each item inside of my Viewport will have a fixed height of 100 pixels, which matches the item size argument in the component itself. And just for bonus points, if you want to make it look cool, this is a good place to customize the pseudo selector for the actual ScrollBar element to give it a different style and size as the user scrolls down. When inside the context of a ScrollViewport, The CDK gives you a structural directive called CDK virtual 4. It replaces ng 4, but it works exactly the same way. If we go ahead and serve the app, what's happening is that the CDK is keeping track of the items in the list and which items will fit into the viewport. A good way to demonstrate this is just by using a plain CSS animation because that animation will be applied each time the item is re rendered. So I'm using a little library here you may have heard of called Animate CSS. And you'll notice as I scroll up and down, the animation is reapplied to each element because it's being re rendered in the DOM. In a regular list, the CSS animation would only be applied on the initial page load. So that's kind of an interesting little side effect with VirtualScroll that you can play with. Now there's a couple of other basic things that you need to know. First of all, the virtual scroll component emits a custom event called Scrolled Index Change. Eventually, we'll use this to implement our infinite Scroll because it emits the index of the item that the user has scrolled to, so we can use that to determine when to pull the next batch of items from the database. Then you'll also notice that this component has its own public API that we can use to do things like measure the viewport or get the total item list size or even dynamically scroll to a specific element. In order to use those methods, you'll need to grab the component from the DOM. And you'll definitely want to strong type it, so we'll import the CDK Virtual Scroll Viewport component And then we can grab it with ViewChild. Then we can set it up as a property called Viewport. And if we start typing this out, you'll notice that it shows us a bunch of different methods that we can use. So that's how you can interact with the component dynamically in your TypeScript code. For example, if you have some chat messages, you could use this to scroll directly to bottom or update the scroll position when new messages are received. Now that you know the basics, we're ready to build out our Infinite Real Time Scroll using Firestore. I created an Infinite Scroll video with Firestore a while back, but today's solution will improve upon it in 2 big ways. First of all, it will make the list real time. So even though we're pulling items in batches, they'll still be updated in real time if the data changes on the back end. And the second obvious improvement is that this will be a virtual scroll, so we should be able to get much better performance as the user scrolls through more and more items. And also the CDK will just make our code much more simple and reliable. Now starting in the component TypeScript, we'll first import AngularFirestore, as well as a bunch of different stuff here from RxJS. We'll pull items from the database in batch sizes of 20. Then we'll set up a property for the viewport itself and then one to determine whether or not we've reached the end of the list. And then a behavior subject to keep track of the offset that we'll use to make our query to Firestore. And it's important that the subject starts with null and not undefined or an empty string because we need that null value to start our query to Firestore. So every time this offset changes, we're going to wanna make a new query to Firestore for the source data. But we only want to do that when the user scrolls to the bottom of the current list. So we can listen to that index change event that I showed you earlier And then we'll run this next batch method which takes the event as well as the very last item in the list as its argument. Then if we've reached the end, then we'll just go ahead and return and pull any additional data. But otherwise, we can take the Viewport rendered range end value and compare that to the total items in the actual list itself. And conveniently, the CDK has a method for that called Get Data Length. So if the ending index equals the total length of the items in the array, then we know that we've reached the end. We can send the next Offset value to our behavior subject, which will reactively pull a new batch of items from the database. And then while I have you here, I'm also going to set up a Track by function which just tracks the index of the items in the Array and only renders the ones that change. That's just a good thing to have for real time data because, otherwise, The entire list would rerender for each item that changes. So now I'm going to break this down into manageable chunks. The first one is getting an individual batch from the database. And to do that, we need to know the last seen item in the previous batch. I've added 200 records to Firestore of the same dummy data that I showed you earlier. And I want to do a paginated query on that data. So first I want to order by the user's name and then we'll start after the offset value, which needs to be null if we're starting a new batch. Otherwise, it needs to be the name of the last user and the last batch. And lastly, we'll limit our query to our batch size which again is 20. From there, we'll go ahead and call Snapshot Changes to get these Document Snapshots as an observable. If this observable emits an empty array, then it's safe to assume that we've reached the end of our collection in the database, at which point we can set the end property to true. Now the tricky part is keeping everything up to date in real time across multiple batches. In order to do that reliably, we need to take the array of documents and then map them to an object where the key is their document ID. D. This allows us to update a document's data anywhere in the list based on its key value instead of its index position. This ensures that Every document in the Array is unique because otherwise we are at risk of adding duplicate documents to the Array if things change between the Queries. So the next thing we wanna do is go into our constructor and set up a source observable that first listens to the offset value and then merges in new batches whenever the Offset changes, which again will happen when the user scrolls to the bottom of a list. That event can be fired multiple times, so I'm going to throttle it by 500 milliseconds just so we're not sending off redundant requests to the back end. Now you'll notice that I'm using Merge Map here to merge a new batch into the stream. It's important to use Merge Map here and not Switch Map because otherwise the previous subscriptions would be canceled, meaning that only the very last batch would be real time. From there, we can pipe in the Scan operator, which will just merge in the current batch with the existing batches. So this Batch Map Observable is our main source of truth because it contains all of the mappings for the individual item keys and their data. But we still want an actual array to loop over in the frontend. So we can go here and define our actual infinite array of objects as this batch map, but mapped to the object values which will give us an array of objects. And that takes care of the main logic for our Infinite Real Time Virtual Scroll. That may have seemed relatively complex, but in the grand scheme of things it's very simple considering what it does. Now we just have a couple of small modifications in the HTML. If you want to show an index in your list, you can just say let I equals index and then we'll also add in our trackBy function that we defined earlier. Then we can listen to that scrolledIndexChange event and the first argument will be the event itself, which is the index number. Then the second argument is the offset value for the query, which in our case will be the name of the very last person in the array. We can actually just parse that value right here in the template. Not the prettiest code in the world, but it works. Then the last thing we need to do is unwrap the infinite observable. My preferred way of doing this is using an NG Container and then unwrapping the Observable with the AsyncPipe. We can then say as people to give us a template variable of the array objects and then we can just use it throughout this HTML. Now if we pull up the demo, you can see that we have all of our objects here in the database And they're queried in the same alphabetical order that we get when we order them on the Firebase console. And if we make any changes to our data on the right, it will be reflected in the UI on the left in real time. It is important to keep in mind that the actual position of the item won't change, and if it's deleted, it won't be removed from the list. But if you need that functionality, that could definitely be made possible with some additional work in the code. Now, I'm pulling up the browser console, and you'll notice that there's an Array being console logged on the right every time we get to a certain event. The array size initially starts at 20, and then as we scroll down, it will We'll change the offset and our array grows from 20 to 40 to 60 and so on. And from the user's perspective, it happened so fast that Looks like this list existed all along, which is exactly what you want with an infinite scroll. But we are keeping track when we get to the end of the list, so when we finally reach that point we'll go ahead and give gives her some indication that there are no items left to retrieve. That's all folks. If this video helps you, please like and subscribe. And if you wanna take your development the next level, consider becoming a pro member at angularfirebase.com. You'll get access to all kinds of exclusive content like full courses and my book designed to help you build ship your app faster. Thanks for watching, and I'll talk to you soon.
        