Virtual Scrolling
By importing ScrollingModule
the newly added CDK can take advantage
of the VirtualScrolling
. When we have to define Virtual Scrolling,
then it is loading or unloading of the DOM elements based on the noticeable aspects
of the list. Now, Google has accelerated the speed of the huge scrollable lists.
Based on the visible parts of a list, virtual Scrolling loads and unloads elements from the DOM and make it possible to build very fast experiences for users with very large scrollable lists.
The <cdk-virtual-scroll-viewport>
displays large lists of elements
efficiently by only providing the items that fit on-screen. Loading hundreds of
elements at a time can be slow in any browser. Virtual scrolling enables an effective
way to simulate all items being provided by making the height of the container element
the same as the height of the total number of elements to be rendered, and then
only providing the items in view. Virtual scrolling is different from strategies
like infinite scroll where it renders a set of elements and then renders the rest
when you hit the end.
cdkScrollable and ScrollDispatcher
The cdkScrollable
directive and the ScrollDispatcher
service
jointly permit components to react to scrolling in any of its ancestor scrolling
containers.
The cdkScrollable
directive must be applied to that element that acts
as a scrolling container and this marks the element as a Scrollable and registers
it with the ScrollDispatcher
. The dispatcher then allows components
to share both event listeners and knowledge of all the scrollable containers in
the application.
ViewportRuler
The ViewportRuler
is a service which will be injected and is used to
measure the bounds of the browser viewport.
Basic virtual scroll
*ngFor
is replaced by *cdkVirtualFor
inside of a <cdk-virtual-scroll-viewport>
, supporting the exact same API as *ngFor
. The simplest usage just
describes the list of items (note that the itemSize property on the viewport must
be set).
Example
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ScrollingModule } from '@angular/cdk/scrolling'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, ScrollingModule, BrowserAnimationsModule ], exports: [ BrowserAnimationsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { items = Array.from({length: 100000}).map((_, i) => `Item #${i}`); }
.example-viewport { height: 200px; width: 200px; border: 1px solid black; margin-top: 150px; margin-left: 50px; } .example-item { height: 50px; }
<cdk-virtual-scroll-viewport itemSize="50" class="example-viewport" > <div *cdkVirtualFor="let item of items" class="example-item" > {{item}} </div> </cdk-virtual-scroll-viewport>
*cdkVirtualFor makes the following context variables available to the template:
Context variable | Description |
---|---|
index
|
The index of the item in the data source. |
count
|
The total number of items in the data source. |
first
|
Whether this is the first item in the data source. |
last
|
Whether this is the last item in the data source. |
even
|
Whether the index is even. |
odd
|
Whether the index is odd. |
All of these apply to the index of the item available in the data source, not to the index of the rendered portion of data.
Example
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { DragDropModule } from '@angular/cdk/drag-drop'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, DragDropModule, BrowserAnimationsModule ], exports: [ BrowserAnimationsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
import { Component } from '@angular/core'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { movies = [ '1. How to upgrade to Angular 7', '2. Angular 7 CLI Prompts', '3. Application Performance', '4. Documentation Updates', '5. Dependency Updates', '6. Drag and Drop', '7. Virtual Scrolling', '8. Improved Accessibility of Selects', '9. Partner Launches', '10. Angular Elements' ]; drop(event: CdkDragDrop<string[]>) { moveItemInArray(this.movies, event.previousIndex, event.currentIndex); } } }
.example-list { width: 500px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: block; background: white; border-radius: 4px; overflow: hidden; margin-left: 300px; margin-top: 50px; } .example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px; } .cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); } .cdk-drag-placeholder { opacity: 0; } .cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); } .example-box:last-child { border: none; } .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); }
<cdk-virtual-scroll-viewport [itemSize]="18 * 7" class="example-viewport"> <div *cdkVirtualFor="let item of items; let index = index; let count = count; let first = first; let last = last; let even = even; let odd = odd;" [class.example-alternate]="odd"> <div class="example-item-detail">Item: {{item}}</div> <div class="example-item-detail">Index: {{index}}</div> <div class="example-item-detail">Count: {{count}}</div> <div class="example-item-detail">First: {{first ? 'Yes' : 'No'}}</div> <div class="example-item-detail">Last: {{last ? 'Yes' : 'No'}}</div> <div class="example-item-detail">Even: {{even ? 'Yes' : 'No'}}</div> <div class="example-item-detail">Odd: {{odd ? 'Yes' : 'No'}}</div> </div> </cdk-virtual-scroll-viewport>
The specification and working of a trackBy
function are same as for the *ngFor
trackBy.
The index passed to the tracking function will be the index in the data source,
not the index in the given portion.
Note:To read more go to material.angular.io