2003 cdk
When dealing with a large amount of content, you may find it useful to add an infinite scroll feature to your site. When I say infinite scrolling, I’m referring to a page appending new content as the user continues to scroll, giving the page the illusion of scrolling indefinitely. Loads of websites use this feature, and it can be a fluid alternative to something like pagination.
当处理大量内容时,您可能会发现向站点添加无限滚动功能很有用。 当我说无限滚动时 ,我指的是随着用户继续滚动而添加新内容的页面,从而给页面带来无限滚动的错觉。 大量的网站都使用此功能,它可以替代分页之类的灵活方式 。
While there are myriad different ways to implement this, let’s explore how we can accomplish this using the Angular Component Dev Kit (CDK).
尽管有许多不同的方法可以实现此目的,但让我们探索如何使用Angular Component Dev Kit(CDK)来完成此任务。
Let’s start by adding the @angular/cdk
package to our project:
首先,将@angular/cdk
包添加到我们的项目中:
$ npm install @angular/cdk
To leverage the infinite scrolling functionality in this package, import ScrollingModule to your app.module.ts
:
要利用此程序包中的无限滚动功能, app.module.ts
ScrollingModule导入到app.module.ts
:
import { ScrollingModule} from '@angular/cdk/scrolling';
Then add it to your imports:
然后将其添加到您的导入中:
imports: [
ScrollingModule
]
You’re now ready to start!
您现在就可以开始了!
We’re going to build a component that displays random historical events. When the user reaches the end of the scroll, our application will load more facts. For the purpose of this tutorial, I’m going to gloss over the details of building the service.
我们将构建一个显示随机历史事件的组件。 当用户到达滚动末尾时,我们的应用程序将加载更多事实。 就本教程而言,我将介绍构建服务的详细信息。
For the time being, assume that we have a FactService that only provides the following function:
暂时,假设我们有一个仅提供以下功能的FactService:
getRandomFact();
We’ll be pulling 10 facts at a time, and each time the user scrolls to the end, we’ll query for 10 more facts.
我们将一次提取10个事实,并且每次用户滚动到末尾时,我们都会查询另外10个事实。
Construct a new component that will act as your infinite scroller. I’m calling mine FactScrollerComponent
. You can use the Angular CLI to do this:
构造一个新组件,将其用作无限滚动器。 我叫我FactScrollerComponent
。 您可以使用Angular CLI执行此操作:
$ ng g component fact-scroller
Ensure that the new component is imported to your app.module.ts
and added to the declarations:
确保将新组件导入到您的app.module.ts
并添加到声明中:
import { ScrollingModule } from '@angular/cdk/scrolling';
declarations: [
FactScrollerComponent
]
In our fact-scroller.component.html
let’s construct the scroller scaffolding:
在我们的fact-scroller.component.html
我们构造滚动器支架:
<cdk-virtual-scroll-viewport itemSize="100">
<li *cdkVirtualFor="let fact of dataSource">
<!-- Print stuff here -->
</li>
</cdk-virtual-scroll-viewport>
Here we use a cdk-virtual-scroll-viewport
to be our virtual scroller. Within this, we loop over our items using *cdkVirtualFor
, which is analogous as using *ngFor
.
在这里,我们使用cdk-virtual-scroll-viewport
作为我们的虚拟滚动条。 在其中,我们使用*cdkVirtualFor
我们的项目,这类似于使用*ngFor
。
In order for the component to properly size its internal scroller, we need to tell the scroller how tall each item will be (in pixels). This is done using the itemSize
directive. So, itemSize="100"
means that item in the list will require 100px of height.
为了使组件能够正确调整其内部滚动条的大小,我们需要告诉滚动条各项的高度(以像素为单位)。 这是使用itemSize
指令完成的。 因此, itemSize="100"
表示列表中的项目将需要100px的高度。
We’ve also told the scroller to pull the data from dataSource
, which doesn’t exist yet, so it’s best we create it now.
我们还告诉滚动器从不存在的dataSource
提取数据,因此最好现在创建它。
In our fact-scroller.component.ts
file, we need to define what our data source looks like. To do this, we’ll extend the DataSource
class in @angular/cdk/collections
. Here’s what our data source looks like:
在我们的fact-scroller.component.ts
文件中,我们需要定义数据源的外观。 为此,我们将在@angular/cdk/collections
扩展DataSource
类。 我们的数据源如下所示:
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
export interface Fact {
text?: string;
date?: string;
}
export class FactsDataSource extends DataSource<Fact | undefined> {
private cachedFacts = Array.from<Fact>({ length: 0 });
private dataStream = new BehaviorSubject<(Fact | undefined)[]>(this.cachedFacts);
private subscription = new Subscription();
constructor(private factService: FactService) {
super();
}
connect(collectionViewer: CollectionViewer): Observable<(Fact | undefined)[] | ReadonlyArray<Fact | undefined>> {
this.subscription.add(collectionViewer.viewChange.subscribe(range => {
// Update the data
}));
return this.dataStream;
}
disconnect(collectionViewer: CollectionViewer): void {
this.subscription.unsubscribe();
}
}
There’s a lot to digest here, so let’s break it down.
这里有很多要消化的东西,所以让我们分解一下。
We first define our model, Fact
, which will define our data structure.
我们首先定义模型Fact
,它将定义我们的数据结构。
Within FactsDataSource
, we need to implement two functions: connect()
, and disconnect()
. The data source is subscribed to any changes in the collection viewer (e.g. the user scrolls), and will then perform an action and return the data stream. We are going to tell the data source to get more data when we have reached the end of the list.
在FactsDataSource
,我们需要实现两个函数: connect()
和disconnect()
。 数据源订阅了集合查看器中的任何更改(例如,用户滚动),然后将执行操作并返回数据流。 当到达列表末尾时,我们将告诉数据源获取更多数据。
We also declared three member variables:
我们还声明了三个成员变量:
cachedFacts
: our cached results,
cachedFacts
:我们的缓存结果,
dataStream
: a RxJS BehaviorSubject to propagate changes to our cached results, and
dataStream
:一个RxJS BehaviorSubject,用于将更改传播到我们的缓存结果,并且
subscription
: a subscription to listen for view collection changes.
subscription
:用于侦听视图集合更改的订阅。
Let’s define a few helpers within this class:
让我们在此类中定义一些帮助器:
private pageSize = 10;
private lastPage = 0;
private _fetchFactPage(): void {
for (let i = 0; i < this.pageSize; ++i) {
this.factService.getRandomFact().subscribe(res => {
this.cachedFacts = this.cachedFacts.concat(res);
this.dataStream.next(this.cachedFacts);
});
}
}
private _getPageForIndex(i: number): number {
return Math.floor(i / this.pageSize);
}
I’m setting the page size to 10, meaning I want to grab 10 facts at a time. I’m also going to keep track of the last page loaded.
我将页面大小设置为10,这意味着我想一次获取10个事实。 我还将跟踪加载的最后一页。
_fetchFactPage()
simply makes a call to our service to get some facts, which are then appended to the cache.
_fetchFactPage()
只需调用我们的服务即可获取一些事实,然后将这些事实附加到缓存中。
_getPageForIndex()
will convert an line index to a page (or batch) value.
_getPageForIndex()
会将行索引转换为页面(或批处理)值。
Putting these all together, we can then define how we want the list to update within the subscription callback:
将所有这些放在一起,然后可以定义如何在订阅回调中更新列表:
connect(collectionViewer: CollectionViewer): Observable<(Fact | undefined)[] | ReadonlyArray<Fact | undefined>> {
this.subscription.add(collectionViewer.viewChange.subscribe(range => {
const currentPage = this._getPageForIndex(range.end);
if (currentPage > this.lastPage) {
this.lastPage = currentPage;
this._fetchFactPage();
}
}));
return this.dataStream;
}
We also want to start with some data, so we can make a call to our fetch function in the constructor:
我们还想从一些数据开始,因此我们可以在构造函数中调用fetch函数:
constructor(private factService: FactService) {
super();
// Start with some data.
this._fetchFactPage();
}
Our custom data source should now get us where we need to go. The final piece to put it all together is to add our new data source to the component.
现在,我们的自定义数据源应该将我们带到需要去的地方。 将所有内容放在一起的最后一部分是将我们的新数据源添加到组件中。
@Component({
selector: 'app-fact-scroller',
templateUrl: './fact-scroller.component.html',
styleUrls: ['./fact-scroller.component.scss']
})
export class FactScrollerComponent {
dataSource: FactsDataSource;
constructor(private factService: FactService) {
this.dataSource = new FactsDataSource(factService);
}
}
And we’re done! Everything from here on out is formatting. I’ve rewritten my HTML to display the facts like so:
我们完成了! 从这里开始的所有内容都在格式化。 我已经重写了HTML以显示如下事实:
<cdk-virtual-scroll-viewport itemSize="100" class="fact-scroll-viewport">
<li *cdkVirtualFor="let fact of dataSource">
<div *ngIf="fact" class="fact-item">
<div class="fact-date">{{ fact.year }}</div>
<div class="fact-text">{{ fact.text }}</div>
</div>
<div *ngIf="!fact">
Loading ...
</div>
</li>
</cdk-virtual-scroll-viewport>
@Component({
selector: 'app-fact-scroller',
templateUrl: './fact-scroller.component.html',
styleUrls: ['./fact-scroller.component.scss']
})
export class FactScrollerComponent {
dataSource: FactsDataSource;
constructor(private factService: FactService) {
this.dataSource = new FactsDataSource(factService);
}
}
Happy Scrolling!
滚动愉快!
翻译自: https://www.digitalocean.com/community/tutorials/angular-infinite-scroll
2003 cdk