Skip to content

Commit

Permalink
refactor(module:list): refactor list (#2548)
Browse files Browse the repository at this point in the history
* refactor(module:list): refactor list

* fix: import scrolling in share module

* chore: add encapsulation:ViewEncapsulation.None
ref #2381
  • Loading branch information
cipchk authored and vthinkxie committed Nov 30, 2018
1 parent 387a90d commit 3c6e835
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 213 deletions.
12 changes: 8 additions & 4 deletions components/list/demo/infinite-load.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
---
order: 6
title:
zh-CN: 滚动加载
en-US: Scrolling loaded
zh-CN: 滚动加载无限长列表
en-US: Infinite & virtualized
---

## zh-CN

结合 [ngx-infinite-scroll](https://github.com/orizens/ngx-infinite-scroll) 实现滚动自动加载列表。
结合 [cdk-virtual-scroll](https://material.angular.io/cdk/scrolling/overview) 实现滚动加载无限长列表,带有虚拟化 virtualization 功能,能够提高数据量大时候长列表的性能。

`Virtualized` 是在大数据列表中应用的一种技术,主要是为了减少不可见区域不必要的渲染从而提高性能,特别是数据量在成千上万条效果尤为明显。

## en-US

The example of infinite load with [ngx-infinite-scroll](https://github.com/orizens/ngx-infinite-scroll).
An example of infinite list & virtualized loading using [cdk-virtual-scroll](https://material.angular.io/cdk/scrolling/overview).

`Virtualized` rendering is a technique to mount big sets of data. It reduces the amount of rendered DOM nodes by tracking and hiding whatever isn't currently visible.
150 changes: 90 additions & 60 deletions components/list/demo/infinite-load.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,108 @@
// tslint:disable:no-any
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd';

const fakeDataUrl = 'https://randomuser.me/api/?results=5&inc=name,gender,email,nat&noinfo';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';

@Component({
selector: 'nz-demo-list-infinite-load',
template: `
<!--<div class="demo-infinite-container"-->
<!--infiniteScroll-->
<!--[infiniteScrollDistance]="2"-->
<!--[infiniteScrollThrottle]="50"-->
<!--(scrolled)="onScroll()"-->
<!--[scrollWindow]="false">-->
<!--<nz-list [nzDataSource]="data" [nzRenderItem]="item">-->
<!--<ng-template #item let-item>-->
<!--<nz-list-item>-->
<!--<nz-list-item-meta-->
<!--[nzTitle]="nzTitle"-->
<!--nzAvatar="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"-->
<!--[nzDescription]="item.email">-->
<!--<ng-template #nzTitle>-->
<!--<a href="https://ng.ant.design">{{item.name.last}}</a>-->
<!--</ng-template>-->
<!--</nz-list-item-meta>-->
<!--</nz-list-item>-->
<!--</ng-template>-->
<!--<nz-spin *ngIf="loading && hasMore" class="demo-loading"></nz-spin>-->
<!--</nz-list>-->
<!--</div>-->
<div>
<cdk-virtual-scroll-viewport
itemSize="73"
class="demo-infinite-container"
>
<nz-list>
<nz-list-item *cdkVirtualFor="let item of ds">
<nz-skeleton
*ngIf="!item"
[nzAvatar]="true"
[nzParagraph]="{ rows: 1 }"
></nz-skeleton>
<nz-list-item-meta
*ngIf="item"
[nzTitle]="nzTitle"
nzAvatar="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
[nzDescription]="item.email"
>
<ng-template #nzTitle>
<a href="https://ng.ant.design">{{item.name.last}}</a>
</ng-template>
</nz-list-item-meta>
</nz-list-item>
</nz-list>
</cdk-virtual-scroll-viewport>
</div>
`,
styles: [ `
:host ::ng-deep .demo-infinite-container {
border: 1px solid #e8e8e8;
border-radius: 4px;
overflow: auto;
padding: 8px 24px;
height: 300px;
}
:host ::ng-deep .demo-loading {
position: absolute;
bottom: -40px;
left: 50%;
}
` ]
styles: [
`
:host ::ng-deep .demo-infinite-container {
height: 300px;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
`
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NzDemoListInfiniteLoadComponent implements OnInit {
data: any[] = [];
loading = false;
hasMore = true;
export class NzDemoListInfiniteLoadComponent {
ds = new MyDataSource(this.http);

constructor(private http: HttpClient) {}
}

constructor(private http: HttpClient, private msg: NzMessageService) {}
class MyDataSource extends DataSource<string | undefined> {
private length = 100000;
private pageSize = 10;
private cachedData = Array.from<any>({ length: this.length });
private fetchedPages = new Set<number>();
private dataStream = new BehaviorSubject<any[]>(this.cachedData);
private subscription = new Subscription();

ngOnInit(): void {
this.getData((res: any) => this.data = res.results);
constructor(private http: HttpClient) {
super();
}

getData(callback: (res: any) => void): void {
this.http.get(fakeDataUrl).subscribe((res: any) => callback(res));
connect(collectionViewer: CollectionViewer): Observable<any[]> {
this.subscription.add(
collectionViewer.viewChange.subscribe(range => {
const startPage = this.getPageForIndex(range.start);
const endPage = this.getPageForIndex(range.end - 1);
for (let i = startPage; i <= endPage; i++) {
this.fetchPage(i);
}
})
);
return this.dataStream;
}

onScroll(): void {
if (this.loading) return;
this.loading = true;
if (this.data.length > 14) {
this.msg.warning('Infinite List loaded all');
this.hasMore = false;
this.loading = false;
disconnect(): void {
this.subscription.unsubscribe();
}

private getPageForIndex(index: number): number {
return Math.floor(index / this.pageSize);
}

private fetchPage(page: number): void {
if (this.fetchedPages.has(page)) {
return;
}
this.getData((res: any) => {
this.data = this.data.concat(res.results);
this.loading = false;
});
this.fetchedPages.add(page);

this.http
.get(
`https://randomuser.me/api/?results=${
this.pageSize
}&inc=name,gender,email,nat&noinfo`
)
.subscribe((res: any) => {
this.cachedData.splice(
page * this.pageSize,
this.pageSize,
...res.results
);
this.dataStream.next(this.cachedData);
});
}
}
77 changes: 45 additions & 32 deletions components/list/demo/vertical.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'nz-demo-list-vertical',
Expand All @@ -7,37 +7,50 @@ import { Component } from '@angular/core';
[nzDataSource]="data"
[nzItemLayout]="'vertical'"
[nzRenderItem]="item"
[nzPagination]="pagination">
<ng-template #item let-item>
<nz-list-item [nzContent]="item.content" [nzActions]="[starAction,likeAction,msgAction]" [nzExtra]="extra">
<ng-template #starAction><i nz-icon type="star-o" style="margin-right: 8px;"></i> 156</ng-template>
<ng-template #likeAction><i nz-icon type="like-o" style="margin-right: 8px;"></i> 156</ng-template>
<ng-template #msgAction><i nz-icon type="message" style="margin-right: 8px;"></i> 2</ng-template>
<nz-list-item-meta
[nzAvatar]="item.avatar"
[nzTitle]="nzTitle"
[nzDescription]="item.description">
<ng-template #nzTitle><a href="{{item.href}}">{{item.title}}</a></ng-template>
</nz-list-item-meta>
<ng-template #extra>
<img width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png">
</ng-template>
</nz-list-item>
</ng-template>
<ng-template #pagination>
<nz-pagination [nzPageIndex]="1" [nzTotal]="50"></nz-pagination>
</ng-template>
[nzPagination]="pagination"
[nzFooter]="footer">
<ng-template #item let-item>
<nz-list-item [nzContent]="item.content" [nzActions]="[starAction,likeAction,msgAction]" [nzExtra]="extra">
<ng-template #starAction><i nz-icon type="star-o" style="margin-right: 8px;"></i> 156</ng-template>
<ng-template #likeAction><i nz-icon type="like-o" style="margin-right: 8px;"></i> 156</ng-template>
<ng-template #msgAction><i nz-icon type="message" style="margin-right: 8px;"></i> 2</ng-template>
<nz-list-item-meta
[nzAvatar]="item.avatar"
[nzTitle]="nzTitle"
[nzDescription]="item.description">
<ng-template #nzTitle><a href="{{item.href}}">{{item.title}}</a></ng-template>
</nz-list-item-meta>
<ng-template #extra>
<img width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png">
</ng-template>
</nz-list-item>
</ng-template>
<ng-template #footer>
<div><b>ant design</b> footer part</div>
</ng-template>
<ng-template #pagination>
<nz-pagination [nzPageIndex]="1" [nzTotal]="50" (nzPageIndexChange)="loadData($event)"></nz-pagination>
</ng-template>
</nz-list>
`
})
export class NzDemoListVerticalComponent {
data = new Array(5).fill({}).map((i, index) => {
return {
href: 'http://ant.design',
title: `ant design part ${index}`,
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
description: 'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content: 'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.'
};
});
}
export class NzDemoListVerticalComponent implements OnInit {
// tslint:disable-next-line:no-any
data: any[] = [];

ngOnInit(): void {
this.loadData(1);
}

loadData(pi: number): void {
this.data = new Array(5).fill({}).map((i, index) => {
return {
href: 'http://ant.design',
title: `ant design part ${index} (page: ${pi})`,
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
description: 'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content: 'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.'
};
});
}
}
6 changes: 6 additions & 0 deletions components/list/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ describe('list', () => {
fixture.detectChanges();
expect(dl.queryAll(By.css('.ant-list-empty-text')).length).toBe(1);
});

it('should be ingore empty text when unspecified data source', () => {
context.data = undefined;
fixture.detectChanges();
expect(dl.queryAll(By.css('.ant-list-empty-text')).length).toBe(0);
});
});

it('#nzGrid', () => {
Expand Down
12 changes: 6 additions & 6 deletions components/list/nz-list-item-meta.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<div *ngIf="isAvatar" class="ant-list-item-meta-avatar">
<div *ngIf="avatarStr || avatarTpl" class="ant-list-item-meta-avatar">
<ng-container *ngIf="avatarStr; else avatarTpl">
<nz-avatar [nzSrc]="avatarStr"></nz-avatar>
</ng-container>
</div>
<div *ngIf="isTitle || isDesc" class="ant-list-item-meta-content">
<h4 *ngIf="isTitle" class="ant-list-item-meta-title">
<ng-container *ngIf="titleStr; else titleTpl">{{ titleStr }}</ng-container>
<div *ngIf="nzTitle || nzDescription" class="ant-list-item-meta-content">
<h4 *ngIf="nzTitle" class="ant-list-item-meta-title">
<ng-container *nzStringTemplateOutlet="nzTitle">{{ nzTitle }}</ng-container>
</h4>
<div *ngIf="isDesc" class="ant-list-item-meta-description">
<ng-container *ngIf="descStr; else descTpl">{{ descStr }}</ng-container>
<div *ngIf="nzDescription" class="ant-list-item-meta-description">
<ng-container *nzStringTemplateOutlet="nzDescription">{{ nzDescription }}</ng-container>
</div>
</div>
39 changes: 5 additions & 34 deletions components/list/nz-list-item-meta.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Component, Input, TemplateRef } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewEncapsulation } from '@angular/core';

@Component({
selector : 'nz-list-item-meta',
templateUrl : './nz-list-item-meta.component.html',
preserveWhitespaces: false,
changeDetection : ChangeDetectionStrategy.OnPush,
encapsulation : ViewEncapsulation.None,
host : {
'[class.ant-list-item-meta]': 'true'
}
})
export class NzListItemMetaComponent {

isAvatar = false;
avatarStr = '';
avatarTpl: TemplateRef<void>;

Expand All @@ -22,39 +23,9 @@ export class NzListItemMetaComponent {
} else {
this.avatarStr = value;
}

this.isAvatar = !!value;
}

isTitle = false;
titleStr = '';
titleTpl: TemplateRef<void>;

@Input()
set nzTitle(value: string | TemplateRef<void>) {
if (value instanceof TemplateRef) {
this.titleStr = null;
this.titleTpl = value;
} else {
this.titleStr = value;
}

this.isTitle = !!value;
}
@Input() nzTitle: string | TemplateRef<void>;

isDesc = false;
descStr = '';
descTpl: TemplateRef<void>;

@Input()
set nzDescription(value: string | TemplateRef<void>) {
if (value instanceof TemplateRef) {
this.descStr = null;
this.descTpl = value;
} else {
this.descStr = value;
}

this.isDesc = !!value;
}
@Input() nzDescription: string | TemplateRef<void>;
}
8 changes: 4 additions & 4 deletions components/list/nz-list-item.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<ng-template #contentTpl>
<div *ngIf="isCon" class="ant-list-item-content" [ngClass]="{'ant-list-item-content-single': metas.length < 1}">
<ng-container *ngIf="conStr; else conTpl">{{ conStr }}</ng-container>
<div *ngIf="nzContent" class="ant-list-item-content" [ngClass]="{'ant-list-item-content-single': metas.length < 1}">
<ng-container *nzStringTemplateOutlet="nzContent">{{ nzContent }}</ng-container>
</div>
</ng-template>
<ng-template #actionsTpl>
<ul *ngIf="nzActions?.length > 0" class="ant-list-item-action">
<li *ngFor="let i of nzActions; let idx = index">
<li *ngFor="let i of nzActions; let last=last;">
<ng-template [ngTemplateOutlet]="i"></ng-template>
<em *ngIf="idx!==nzActions.length-1" class="ant-list-item-action-split"></em>
<em *ngIf="!last" class="ant-list-item-action-split"></em>
</li>
</ul>
</ng-template>
Expand Down
Loading

0 comments on commit 3c6e835

Please sign in to comment.