带有远程数据的虚拟滚动。 Angular 2

时间:2017-01-23 11:17:48

标签: angular kendo-ui-angular2

我们需要使用虚拟滚动来实现kendo网格,但我们需要从服务器获取数百万个数据,因此我们无法将所有数据都放入同一个请求中。 我关注这些文章(http://www.telerik.com/kendo-angular-ui/components/grid/configuration/#toc-scroll-modeshttp://www.telerik.com/kendo-angular-ui/components/grid/data-binding/),但分页的虚拟滚动无效。

我的代码:

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 个答案:

答案 0 :(得分:0)

虚拟滚动和分页无法正常工作。例如;如果要在索引0到10之间的网格上呈现数据,则应至少从服务器中获取30个数据。因为您不希望每次滚动操作都从服务器获取数据。 (跳过:1接受:11,跳过:2接受:12,跳过:3接受:13 ...)

那我们该怎么办?我们需要以30个块的形式获取数据,但应在网格中呈现10行。我们的页面大小为10,但实际页面大小应为30。(这里的数字30是我们页面大小的3倍。没关系。您可以使用4倍,5倍或您想要的大小。)再一次这个场景。