I'm developing an Angular 2 app which must to show a large amount of data in a scrollable
table.
The data is in a array in my component class:
export class AppComponent {
clients: any[] = [];
constructor(){
for (var i = 0; i < 10; ++i) {
this.clients.push({
id: i,
name: 'Client' + i,
address: 'Address of client ' + i,
phone: '(51) 5454-4646'
});
}
}
}
Then the data is loaded through a ngFor in my layout:
<div style="height:600px;overflow:scroll">
<data-table>
<th>Id</th>
<th>Name</th>
<th>Address</th>
<th numeric>Phone</th>
<tr *ngFor="let client of clients">
<td>{{ client.id }}</td>
<td>{{ client.name }}</td>
<td>{{ client.address }}</td>
<td>{{ client.phone}}</td>
</tr>
</data-table>
</div>
For example, It must load data in a table with 10.000, maybe, 20.000 rows, and around 10 columns. The problem here is that it is very slow to load and after load it runs with a terrible lag.
I cannot use pagination in this case and the scroll is mandatory. I would like to keep the scroll thumb size and position as if all data is loaded even if I need to do a trick like load just part of the data.
I would like to know what is the best praticce to do this task without slowdowns.
I would recomand you to convert your table into a component, and to change the change detection strategy into "on push".
That way, Angular will reduce performance cost.
As you mentioned, this is quite a lot of data and will be expensive to the users as the browser's will have to request and download a large amount of data.
I would recommend breaking the data down into subsections and adding pagination. Only show about 100 records per page. It will improve readability and the user experience as well.
In Angular 1.x.x, you could use the limitTo
filter to limit the results. In Angular 2.x.x, you can use the slice
filter.
Your updated template might look something like this. I can't guarantee this will work, but it will show the basic approach.
<th>Id</th>
<th>Name</th>
<th order="desc">Address</th>
<th numeric>Phone</th>
<th>Ações</th>
<tr *ngFor="let client of clients | slice:pageStart:100">
<td>{{ client.id }}</td>
<td>{{ client.name }}</td>
<td>{{ client.address }}</td>
<td>{{ client.phone}}</td>
<td>Ações</td>
</tr>
<button *ngIf="pageStart >= 100" (click)="prevData()">Previous</button>
<button *ngIf="pageStart < clients.length" (click)="NextData()">Next</button>
</data-table>
The component would look something like this
export class DataTable {
// Code omitted for brevity
pageStart = 0;
nextData() {
this.pageStart += 100; // Get the next 100 records
}
prevData() {
this.pageStart -= 100; // Get the previous 100 records
}
}
Alternatively you could use some form of infinite scrolling
.
A little bit late, but i hope i can help other people coming by.
I am using the pagination example by Richard Hamilton with some changes.
Alternatively you could use some form of infinite scrolling.
<div style="height:600px;overflow:scroll" (scroll)="onScroll($event)">
<data-table>
<th>Id</th>
<th>Name</th>
<th>Address</th>
<th numeric>Phone</th>
<tr *ngFor="let client of clients | slice:pageStart:pageEnd">
<td>{{ client.id }}</td>
<td>{{ client.name }}</td>
<td>{{ client.address }}</td>
<td>{{ client.phone}}</td>
</tr>
</data-table>
</div>
The only differences are a scroll eventlistener and no pagination buttons.
export class DataTable {
pageStart:number = 0;
pageEnd:number = 100;
pageHeight:number = 30;
pageBuffer:number = 100;
onScroll( event, doc )
{
const scrollTop = event.target.scrollTop;
const scrollHeight = event.target.scrollHeight;
const offsetHeight = event.target.offsetHeight;
const scrollPosition = scrollTop + offsetHeight;
const scrollTreshold = scrollHeight - this.pageHeight;
if( scrollPosition > scrollTreshold ){
this.pageEnd+= this.pageBuffer;
}
}
}
The script checks the position and will adjust the pageEnd until it reaches the end.
At some point this solution will also slow down.
I only needed the infinite scrolling solution, so i can only tell you my idea about a solution without the slow down problem.
To prevent this problem you need to make sure the rowHeight is fixed.
Now you can calculate the maximum height of all rows.
Create a additional element with the height to make sure the scrolling is availible.
Set position:fixed; at the Data-Table.
This makes sure the Table is always visible.
Now calculate the position for pageStart and pageEnd.
Slice will take care of the rendering.
I think this could work, but i have not tested it yet.
I also don't know if this will be a good solution for daily use.
Good luck at all. :)