-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
virtual-scroll: integrate with relevant existing components #10122
Comments
Hi, thanks for the awesome virtual scroll feature. |
I managed to implement virtual scroll with the table, working pretty amazing out of the box with both fixed and auto strategies. I used the original I added 1 more strategy - I had some issues, most of them were easily solved, the most difficult thing to expect is the handling of meta rows, which are header and footer rows. Handling of headers and footers require special treatment because they are not part of the datasource and thus mess up the entire flow, once you have multiple header/footer rows it get's messy quickly. I solved all issues with size measurements when working with multi-header/footer table. The only issues i'm facing now is sticky rows, which does not work because the virtual table now has a container that "offsets" the sticky row so position top 0 is no longer 0 after it was translated by the parent. Working around this should also be easy if I had access to the function that sets the offset - I dont because it's private in |
I think integrating it with the Grid list would also be a great feature. |
@shlomiassaf can you share the code you used or try a PR? |
I needed Virtual Scroll in MatTree component, I hacked the MatList component to emulate behavior of MatTree as it already has the virtual scrolling capability. I did have to manage the indentations of individual nodes myself though. |
@IlCallo It would be difficult to share it right now, I need to clean some IP stuff from there. The virtual scroll is also a part of a table component (on top of CdkTable) so it has some things that will not work as they are part of that table eco-system, things like global configuration, plugin integration etc... I can confirm that it works, and works quite fast! for both Auto and Fixed size strategies. For now, I will try to describe how I did it:
First, the general layout we will use: <cdk-virtual-scroll-viewport>
<cdk-table></cdk-table>
</cdk-virtual-scroll-viewport> The virtual scroll is external to the table. Now we need to take care of 3 topics:
Adjusting
|
I managed to upload a small demo app I have for the table.... https://shlomiassaf.github.io/table-demo Look at the "Demo" link on the left, it shows a large list with a virtual scroll (AUTO). You can set it to 100,000 rows about 23-24 columns. The "All In One" link shows a virtual scroll with FIXED strategy. It's a POC for all of you that want to use it.
|
OK, also managed to implement Drag and Drop using See demo: https://shlomiassaf.github.io/table-demo It does both column and row d&d. There is no "real" need to create a custom Most of it is private and some functions are just big, if they port most to protected and split some functions (mainly |
it is possible to use virtual scroll with a grid list of element?. |
Hello @shlomiassaf , can you provide source code of your demo application? We need to figure out how to implement features that you show in demo. Thanks |
Hi, It would be really great if you can share the code, Thanks |
@shlomiassaf Thanks for the detailed summary! That will be a great starting point for exploring integration with the table |
@shlomiassaf is there a way to check neg-table in your demo to experiment? |
I got a basic version of virtual scroll working with the grid. I'll give a brief overview of what I did and try to come back and post a working example in a bit. I tried to follow what @shlomiassaf did and I ended up with a slightly different approach. For the following code, I "borrowed" heavily from the material table examples. I'll try to describe what I did here and then just leave the code below to hopefully help answer any questions that my explanation leaves. For the HTML, I wrapped the I created my own strategy for dealing with the virtual scroll in the table and it's mostly just an exceedingly simplified version of the The component stitches the data in the table and the strategy together and creates the separate That's basically what I've done and it is working pretty well for me. If anyone has any insight into getting the table.component.html<cdk-virtual-scroll-viewport [style.height.px]="gridHeight">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef> No. </th>
<td mat-cell *matCellDef="let element"> {{element.position}} </td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef> Weight </th>
<td mat-cell *matCellDef="let element"> {{element.weight}} </td>
</ng-container>
<ng-container matColumnDef="symbol">
<th mat-header-cell *matHeaderCellDef> Symbol </th>
<td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<ng-template let-row matRowDef cdkVirtualFor [matRowDefColumns]="displayedColumns"[cdkVirtualForOf]="rows">
<tr mat-row></tr>
</ng-template>
</table>
</cdk-virtual-scroll-viewport> table-vs-strategy.service.ts@Injectable()
export class TableVirtualScrollStrategy implements VirtualScrollStrategy {
private scrollHeight!: number;
private scrollHeader!: number;
private readonly indexChange = new Subject<number>();
private viewport: CdkVirtualScrollViewport;
public scrolledIndexChange: Observable<number>;
constructor() {
this.scrolledIndexChange = this.indexChange.asObservable().pipe(distinctUntilChanged());
}
public attach(viewport: CdkVirtualScrollViewport): void {
this.viewport = viewport;
this.onDataLengthChanged();
this.updateContent(viewport);
}
public detach(): void {
// no-op
}
public onContentScrolled(): void {
this.updateContent(this.viewport);
}
public onDataLengthChanged(): void {
this.viewport.setTotalContentSize(this.viewport.getDataLength() * this.scrollHeight);
}
public onContentRendered(): void {
// no-op
}
public onRenderedOffsetChanged(): void {
// no-op
}
public scrollToIndex(index: number, behavior: ScrollBehavior): void {
// no-op
}
public setScrollHeight(rowHeight: number, headerHeight: number) {
this.scrollHeight = rowHeight;
this.scrollHeader = headerHeight;
this.updateContent(this.viewport);
}
private updateContent(viewport: CdkVirtualScrollViewport) {
const newIndex = Math.max(0, Math.round((viewport.measureScrollOffset() - this.scrollHeader) / this.scrollHeight) - 2);
viewport.setRenderedContentOffset(this.scrollHeight * newIndex);
this.indexChange.next(
Math.round((viewport.measureScrollOffset() - this.scrollHeader) / this.scrollHeight) + 1
);
}
} table.component.ts@Component({
providers: [{
provide: VIRTUAL_SCROLL_STRATEGY,
useClass: TableVirtualScrollStrategy
}],
...
})
export class TableComponent implements OnInit {
// Manually set the amount of buffer and the height of the table elements
static BUFFER_SIZE = 3;
rowHeight = 48;
headerHeight = 56;
rows: Observable<Array<any>> = of(new Array(1000).fill({position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'}));
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
dataSource: Array<any>;
gridHeight = 400;
constructor(@Inject(VIRTUAL_SCROLL_STRATEGY) private readonly scrollStrategy: TableVirtualScrollStrategy) {}
public ngOnInit() {
const range = Math.ceil(this.gridHeight / this.rowHeight) + TableComponent.BUFFER_SIZE;
this.scrollStrategy.setScrollHeight(this.rowHeight, this.headerHeight);
this.dataSource = combineLatest([this.rows, this.scrollStrategy.scrolledIndexChange]).pipe(
map((value: any) => {
// Determine the start and end rendered range
const start = Math.max(0, value[1] - GridComponent.BUFFER_SIZE);
const end = Math.min(value[0].length, value[1] + range);
// Update the datasource for the rendered range of data
return value[0].slice(start, end);
})
);
}
} |
@nahgrin I hope you don't mind, I've thrown your code into StackBlitz so that I could play around with it. https://stackblitz.com/edit/nahgrin-virtual-scroll-table There was a tiny bit of cleanup, but it pretty much just worked. Thanks so much! |
@garrettld Awesome! Thanks a bunch for doing that! |
@nahgrin Nice work! The problem is in the header/footer rows, you need to take them into account when you calculate the range from the data source. For example, if I have 5 header rows and 1000 items. When i'm in 0 scroll offset I can't return 10 rows because 5 are used by headers... same goes for footer rows. You need to calculate the header/footer rows and their visible height in the view, remove that and use the height left to calculate what is the actual range. Here's an example with 5 header rows that breaks it: Of course there are more things to take care of, sticky rows and AutoSize strategy... You might want to refactor On that directive you can define all the heights... and you can also use a special input for the datasource, the directive will take that datasource and assign it to the table so it's a real plugin. |
@nahgrin Tremendous work. Thanks for your efforts. |
Any news about this would be very much appreciated well....some "news" can be found here: |
I hope this page implements virtual scroll as it could be still open in 20 years... |
Hello guys, anything new? this ticket has 5 years already. |
Hello, note that with material 3 color enhancements, angular mat autocomplete now colours the selected value. Having a cdk virtual scroll viewport inside a mat autocomplete to handle mat options, is now colouring randomly some values. I don't know how the selected value is recognized, but this is weird |
@alixroyere I just ran into this yesterday, the 'randomness' seems that if say, the 2nd item in your mat-option list is selected, that when the virtual scroll happens, the 2nd item in the next batch remains selected. |
5 years and nothing, wow! |
Based on the linked project, it's moving along, just taking time. |
I've been using it for trees for a long time. |
I cannot believe this ticket has been open for 5 years... |
We're going for 6 years! :D |
6 years and 12 'major' releases. Personally I think they should start slowing the version rolling and focusing on quality a bit more... |
I think it is still alive as a joke. You can take a look at the documentation https://material.angular.io/cdk/scrolling/overview you will see that is is pretty flexible to adapt it to most of the use cases. |
Would the new defer also provide options to help with viewport optimisations? For example, only render a column in a table when its in viewport? |
They never cared about quality, why should they start now.... |
I've watched this issue for many years as I wanted to use Of course it would be absolutely great if someone would integrate virtual-scroll in the other cdk components. The fact that nothing happened in the past 6 years states two facts: Inside Google this is probably not asked for, so nobody contributes from that side. We, the community, need to accept that as a fact. Nobody ever promised us this, so why are some of us pretending as if they did? What's left? The community should fill that gap and may contribute something. Instead, I see folks venting their frustration instead of helping moving forward with that topic. In this gigantic thread (150 comments today), there are only three persons, @shlomiassaf & @nahgrin & @piernik, who ever tried. Everyone else? Asking, begging, or worse, complaining. You people are a burden to the open source community. You take and have zero respect for its maintainers and the work they put into it. As said, I don't rely on
Here the two contributions that made it into a dedicated package or repo for some updated visibility:
Lastly, if I were maintaining this issue list I would immediately close this issue. I have respect for the original motivation to create the issue. Looking at it today, I see an issue being too wide, too unspecific. Over the years, it degenerated into a honeypot for unspecific comments. Whoever may try acting on a single topic (like the mentioned folks with their contributions) it gets buried quickly beneath complaints or begging hiding the so much needed exposure for those contributions. Maybe @andrewseguin (who unassigned @mmalerba) could help here, close it with some guidance and recommendations? Or someone else from the maintaining team? |
Closing unsolved issues serves no purpose other than to hide/ignore issues. Someday, someone will have enough time, or get annoyed enough to pick up the issue and resolve it completely, or, people will pick up pieces one at a time and work them. But they won't find it if it is a closed issue. Just because you don't have a need or desire to address the issue yourself, and now find the commentary annoying and don't want to read about it; how about you just roll your eyes and move on to another topic? |
Hi guys, |
Google team, you can do it! |
I started working on a new project at work last week which uses Angular Material. Instantly noticed a bug where a Needless to say, I'm very surprised that this issue is still open after such a long time and that a feature this basic is not part of Angular Material already. |
Hi guys, |
Any updates available on the priority of this featureset? Or, should we consider this to not be happening anytime soon. |
I think you know the answer |
+1 Note I would love to work on this but I'm not even sure who I would go to, to work on this one. I've noticed a pull request here already, but for some reason hasn't been worked on for close to 2 years now. I am trying to pick up the pull request and see if can get over the finish line: #21708 (comment) Also, I created a standalone repo, that uses zoneless, standalone and latest Angular 18 that solves this through and through for anyone interested: https://github.com/CharlieGreenman/angular-material-virtual-scroll-cdk-table. Inspired by much discussed above and should be considered better than all solutions above(until potentially a better one comes along). It is production ready with zero bugs. Update[12/2/2024]: For drag and drop use Update[12/10/2024]: I wanted more performance benefits that the angular material data table did not offer. I ended up going with ag-grid for areas I need higher performance and material data table where not needed, as material data table is simpler to build/more intuitive in angular setting. I suspect this will be my last update. [Side note: I created a pipe that can use to dynamically determine viewport size based on row height. Included in the repo] |
This issue is the most upvoted of the whole angular/components repo (by quite a lot), even more than any issue in the angular repo. It's also from 2018, so quite old. Why is it that no interest has been shown from the team? From what I understand the goal with yearly questionaries and votes is to advance and complete the most desired features, yet this one has been forgotten? |
Integrate virtual-scrolling as an optional add-on for relevant existing components, including:
Part of each integration should include adding docs examples of how to set it up
The text was updated successfully, but these errors were encountered: