【问题标题】:Virtual scroll with remote data. Angular 2带有远程数据的虚拟滚动。角 2
【发布时间】:2017-01-23 11:17:48
【问题描述】:

我们需要用虚拟滚动来实现剑道网格,但是我们需要从服务器获取数百万的数据,所以我们不能将它们全部放入同一个请求中。 我关注了这些文章(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)
            }));
      }
}

【问题讨论】:

标签: angular kendo-ui-angular2


【解决方案1】:

虚拟滚动和分页无法按预期协同工作。例如;您希望在网格上渲染数据,索引 0 介于 10 之间。您应该从服务器获取至少 30 个数据。因为您不应该希望每次滚动操作都从服务器获取数据。 (跳过:1 次拍摄:11,跳过:2 次拍摄:12,跳过:3 次拍摄:13 ...)

那么我们应该怎么做呢?我们需要在 30 个块中获取数据,但应该在网格中呈现 10 行。我们的页面大小为 10,但实际页面大小应为 30。(这里,数字 30 是我们页面大小的 3 倍。没关系。你可以使用 4 次、5 次或你想要的。)在每个滚动动作上我们应该计算又是这个场景。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 1970-01-01
    相关资源
    最近更新 更多