0
votes

We need to implement the kendo grid with virtual scroll, but we need to get millions of data from the server, so we can't get all of them into the same request. I follow those articles (http://www.telerik.com/kendo-angular-ui/components/grid/configuration/#toc-scroll-modes, http://www.telerik.com/kendo-angular-ui/components/grid/data-binding/) but virtual scroll with pagination doesn't work properly.

My code:

msa-grid.component.ts

import { Component, OnInit, ViewChild, ElementRef, Input } from '@angular/core';
import { products } from './products';
import { MsaGridCol } from './MsaGridCol';
import { MsaGridService } from './services/msa-grid.service';
import { Observable, BehaviorSubject } from 'rxjs/Rx';
import * as $ from 'jquery';

import {
    GridDataResult,
    PageChangeEvent
} from '@progress/kendo-angular-grid';

import {

} from '@progress/kendo-data-query';

@Component({
  selector: 'msa-grid',
  templateUrl: './msa-grid.component.html',
  styleUrls: ['./msa-grid.component.scss']
})
export class MsaGridComponent implements OnInit {

    //The data to view
    @Input() private gridData: Array<any> | Observable<GridDataResult>;
    //The selection option
    @Input() private selectionType: string = null;
    //The table columns to show
    @Input() private gridCols: Array<MsaGridCol> = new Array<MsaGridCol>();
    //If the data has to be get from remote or not
    @Input() private remote: boolean = true;
    //If true divide data in pages with paginator
    @Input() private pageable: boolean = false;
    //Scrolling: if scrollable = 'virtual' pageable go to false
    @Input() private scrollable: string = null;
    //Set the grid height
    @Input() private height: number = null;
    //Page Size: THe page size if pageable or virtual-scrolling are enable
    @Input() private pageSize: number = 12;
    //Service Url: Your service data url
    @Input() private serviceUrl: string = "";
    //The records to skip
    private skip: number = 0;
    //The selected items
    private selectedItems: Array<any> = new Array<any>();
    //The table template
    @ViewChild("msaGridTemplate") msaGridTemplate: ElementRef;

    constructor(private msaGridService: MsaGridService) { 
    }

    ngOnInit() {
        if (this.scrollable == 'virtual') {
            this.pageable = false;
        }
        if (this.remote) {
            this.gridData = this.msaGridService;
            this.pageChange({ skip: this.skip, take: this.pageSize });
        }
    }

    //Function that select (or deselect) the clicked record into the table
    selectItem(event, product) {
        if (event.currentTarget.checked) {
            if (this.selectionType == 'single') {
                $(this.msaGridTemplate.nativeElement).find(".k-state-selected").find(".tableSelectionCheckbox").prop("checked", false);
                $(this.msaGridTemplate.nativeElement).find(".k-state-selected").removeClass("k-state-selected");
                this.selectedItems = new Array<any>();
            }
            this.selectedItems.push(product);
            $(event.currentTarget).closest("tr").addClass("k-state-selected");
        } else {
            this.selectedItems = this.selectedItems.filter(el => !(JSON.stringify(el) == JSON.stringify(product)));
            $(event.currentTarget).closest("tr").removeClass("k-state-selected");
        }
        console.log("SELECTED ITEMS", this.selectedItems.map((el) => el.ProductID));
    }

    //Function that select (or deselect) all the records into the table
    selectAllItem(event) {
        if (event.currentTarget.checked) {
            $(this.msaGridTemplate.nativeElement).find(".k-grid-content tr").each((index, el) => {
                if (!$(el).hasClass("k-state-selected")) {
                    $(el).addClass("k-state-selected")
                    $(el).find(".tableSelectionCheckbox").prop('checked', true);
                }
                this.selectedItems = JSON.parse(JSON.stringify(this.gridData));
            });
        } else {
            $(this.msaGridTemplate.nativeElement).find(".k-grid-content tr").each((index, el) => {
                if ($(el).hasClass("k-state-selected")) {
                    $(el).removeClass("k-state-selected");
                    $(el).find(".tableSelectionCheckbox").prop('checked', false);
                }
                this.selectedItems = new Array<any>();
            });
        }
    }

    //When a change page is detected
    protected pageChange(event: PageChangeEvent): void {
        if (this.remote) {
            this.skip = event.skip;
            this.msaGridService.query({ skip: this.skip, take: this.pageSize });
        }
    }

}

msa-grid.component.html

<div #msaGridTemplate>
    <kendo-grid [data]="gridData | async" 
                [height]="370" 
                [selectable]="false" 
                [pageable]="false"
                [scrollable]="'virtual'"
                [skip]="skip"
                [pageSize]="pageSize"
                (pageChange)="pageChange($event)">
        <kendo-grid-column field="SelectRows" title="ID" width="40" *ngIf="selectionType">
            <template kendoGridHeaderTemplate >
                <span><input type="checkbox" *ngIf="selectionType == 'multiple'" (change)="selectAllItem($event)" /></span>
            </template>
            <template kendoGridCellTemplate let-dataItem *ngIf="selectionType">
                <input class="tableSelectionCheckbox" type="checkbox" (change)="selectItem($event, dataItem)" />
            </template>
        </kendo-grid-column>
        <kendo-grid-column *ngFor="let col of gridCols" field="{{col.field}}" title="{{col.title}}" width="{{col.width}}">
            <template kendoGridHeaderTemplate *ngIf="col.headerTemplate">
                <msa-template-wrapper-component [componentType]="col.headerTemplate" [dataItem]="dataItem"></msa-template-wrapper-component>
            </template>
            <template kendoGridCellTemplate let-dataItem *ngIf="col.cellTemplate">
                <msa-template-wrapper-component [componentType]="col.cellTemplate" [dataItem]="dataItem[col.field]"></msa-template-wrapper-component>
            </template>
        </kendo-grid-column>
    </kendo-grid>
</div>

msa-gris.service.ts

import { Component, ViewChild, Input, OnInit, Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable, BehaviorSubject } from 'rxjs/Rx';

import {
    GridComponent,
    GridDataResult,
    DataStateChangeEvent
} from '@progress/kendo-angular-grid';

import {
    toODataString
} from '@progress/kendo-data-query';

@Injectable()
export class MsaGridService extends BehaviorSubject<GridDataResult>{
    private BASE_URL: string = 'http://services.odata.org/V4/Northwind/Northwind.svc/';
    private tableName: string = "Customers";

    constructor(private http: Http) {
        super(null);
    }

    query(state): void {
        this.fetch(this.tableName, state)
            .subscribe(x => super.next(x));
    }

    fetch(tableName: string, state: any): Observable<GridDataResult> {
        const queryStr = `${toODataString(state)}&$count=true`;
        return this.http
            .get(`${this.BASE_URL}${tableName}?${queryStr}`)
            .map(response => response.json())
            .map(response => (<GridDataResult>{
                data: response.value,
                total: parseInt(response["@odata.count"], 10)
            }));
      }
}
1
Can you define "Doesn't work properly?" / add a plunker? I've implemented it, but it has several issues so we're going for classic pagination for the moment. Imo if you've got millions of lines you don't need virtual scroll but infinite scroll, which isn't supported out of the box.Vincent V.
If you want Telerik to add the infinite scroll feature, feel free to upvote this : kendoui-feedback.telerik.com/forums/…Vincent V.
Check out this Observable-based implementation of virtual scroll: github.com/dinony/od-virtualscroll which also supports dynamic fetching (See examples)dinony
Old post but still find questions related to this, from the documentation these are the required properties to make the virtual scroll work properly: ![enter image description here](i.stack.imgur.com/l9LUV.png) Virtual scroll documentation: telerik.com/kendo-angular-ui/components/grid/scroll-modes/…abarrenechea

1 Answers

0
votes

virtual scroll and pagination does not work together you expected. for example; you want render data on your grid with index 0 between 10. you should fetch minimum 30 data from your server. because you shouldnt want fetch data from server every scroll action. (skip:1 take:11, skip:2 take: 12, skip:3 take:13 ...)

so what should we do? we need fetch the data in 30 blocks but should render 10 rows in grid. our page size 10 but real page size should be 30. (here, the number 30 is 3 times our page size. it doesnt matter. you can use 4 times, 5 times or what you want.) on every scroll action we should calculate again again this scenerio.