Angular Grid のリモートデータ操作
Ignite UI for Angular Grid は、リモート仮想化、リモート ソート、リモート フィルタリングなどのリモート データ操作をサポートします。これにより、開発者はこれらのタスクをサーバー上で実行し、生成されたデータを取得して Grid に表示できます。
Angular Grid リモート データ操作の例
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { RemoteFilteringSampleComponent } from "./grid/grid-remote-filtering-sample/remote-filtering-sample.component" ;
import {
IgxGridModule,
IgxBadgeModule,
IgxToastModule
} from "igniteui-angular" ;
import { HttpClientModule } from "@angular/common/http" ;
import { RemoteFilteringService } from "./services/remoteFiltering.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
RemoteFilteringSampleComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxBadgeModule,
HttpClientModule,
IgxToastModule
],
providers : [RemoteFilteringService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { ChangeDetectorRef, Component, OnInit, ViewChild, AfterViewInit, OnDestroy } from '@angular/core' ;
import { IgxGridComponent, NoopFilteringStrategy, NoopSortingStrategy } from 'igniteui-angular' ;
import { Subject } from 'rxjs' ;
import { debounceTime, takeUntil } from 'rxjs/operators' ;
import { RemoteFilteringService } from '../../services/remoteFiltering.service' ;
const DEBOUNCE_TIME = 300 ;
@Component ({
providers : [RemoteFilteringService],
selector : 'app-grid-remote-filtering-sample' ,
styleUrls : ['./remote-filtering-sample.component.scss' ],
templateUrl : './remote-filtering-sample.component.html'
})
export class RemoteFilteringSampleComponent implements OnInit , AfterViewInit , OnDestroy {
@ViewChild ('grid' , { static : true }) public grid: IgxGridComponent;
public remoteData: any ;
public noopFilterStrategy = NoopFilteringStrategy.instance();
public noopSortStrategy = NoopSortingStrategy.instance();
private _prevRequest: any ;
private _chunkSize: number ;
private destroy$ = new Subject<boolean >();
constructor (private _remoteService: RemoteFilteringService, public cdr: ChangeDetectorRef ) { }
public ngOnInit(): void {
this .remoteData = this ._remoteService.remoteData;
}
public ngAfterViewInit ( ) {
const filteringExpr = this .grid.filteringExpressionsTree.filteringOperands;
const sortingExpr = this .grid.sortingExpressions[0 ];
this ._chunkSize = Math .ceil(parseInt (this .grid.height, 10 ) / this .grid.rowHeight);
this .grid.isLoading = true ;
this ._remoteService.getData(
{
chunkSize : this ._chunkSize,
startIndex : this .grid.virtualizationState.startIndex
},
filteringExpr,
sortingExpr,
(data ) => {
this .grid.totalItemCount = data['@odata.count' ];
this .grid.isLoading = false ;
});
this .grid.dataPreLoad.pipe(
debounceTime(DEBOUNCE_TIME),
takeUntil(this .destroy$)
).subscribe(() => {
this .processData();
});
this .grid.filteringExpressionsTreeChange.pipe(
debounceTime(DEBOUNCE_TIME),
takeUntil(this .destroy$)
).subscribe(() => {
this .processData(true );
});
this .grid.sortingExpressionsChange.pipe(
debounceTime(DEBOUNCE_TIME),
takeUntil(this .destroy$)
).subscribe(() => {
this .processData();
});
}
public processData (isFiltering: boolean = false ) {
if (this ._prevRequest) {
this ._prevRequest.unsubscribe();
}
if (!this .grid.isLoading) {
this .grid.isLoading = true ;
}
const virtualizationState = this .grid.virtualizationState;
const filteringExpr = this .grid.filteringExpressionsTree.filteringOperands;
const sortingExpr = this .grid.sortingExpressions[0 ];
if (isFiltering) {
virtualizationState.startIndex = 0 ;
}
this ._prevRequest = this ._remoteService.getData(
{
chunkSize : this ._chunkSize,
startIndex : virtualizationState.startIndex
},
filteringExpr,
sortingExpr,
(data ) => {
this .grid.totalItemCount = data['@odata.count' ];
if (this .grid.isLoading) {
this .grid.isLoading = false ;
}
});
}
public formatNumber (value: number ) {
return value.toFixed(2 );
}
public formatCurrency (value: number ) {
return '$' + value.toFixed(2 );
}
public ngOnDestroy ( ) {
if (this ._prevRequest) {
this ._prevRequest.unsubscribe();
}
this .destroy$.next();
this .destroy$.complete();
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid [data ]="remoteData | async" [height ]="'460px'" [width ]="'100%'" [autoGenerate ]='false'
[filterStrategy ]="noopFilterStrategy"
[sortStrategy ]="noopSortStrategy"
[allowFiltering ]="true" >
<igx-column [field ]="'ProductID'" [sortable ]="true" [filterable ]="false" > </igx-column >
<igx-column [field ]="'ProductName'" [sortable ]="true" > </igx-column >
<igx-column [field ]="'UnitPrice'" dataType ="number" [formatter ]="formatCurrency" [sortable ]="true" > </igx-column >
<igx-column [field ]="'UnitsInStock'" dataType ="number" [headerClasses ]="'headerAlignSyle'" [sortable ]="true" >
<ng-template igxHeader >
<span class ="cellAlignSyle" > UnitsInStock</span >
</ng-template >
<ng-template igxCell let-val >
<div class ="currency-badge-container" >
<igx-badge *ngIf ="val>50" type ="success" position ="bottom-right" icon ="arrow_upward" class ="badge-left" > </igx-badge >
<igx-badge *ngIf ="val<=50" type ="error" position ="bottom-right" icon ="arrow_downward" class ="error badge-left" > </igx-badge >
<span class ="cellAlignSyle" [class.up ]="val>50" [class.down ]="val<=50" > {{ formatNumber(val) }}</span >
</div >
</ng-template >
</igx-column >
<igx-column [field ]="'QuantityPerUnit'" [sortable ]="true" > </igx-column >
<igx-column [field ]="'ReorderLevel'" dataType ="number" [headerClasses ]="'headerAlignSyle'" [sortable ]="true" >
<ng-template igxHeader >
<span class ="cellAlignSyle" > ReorderLevel</span >
</ng-template >
<ng-template igxCell let-val >
<div class ="currency-badge-container" >
<igx-badge *ngIf ="val>20" type ="success" position ="bottom-right" icon ="arrow_upward" class ="badge-left" > </igx-badge >
<igx-badge *ngIf ="val<=20" type ="error" position ="bottom-right" icon ="arrow_downward" class ="error badge-left" > </igx-badge >
<span class ="cellAlignSyle" [class.up ]="val>0" [class.down ]="val<=0" > {{ formatNumber(val) }}</span >
</div >
</ng-template >
</igx-column >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
.cellAlignSyle {
text-align : right;
float :right ;
}
.cellAlignSyle > span {
float :right ;
}
.up {
color : green;
}
.down {
color : red;
}
.headerAlignSyle {
text-align : right !important ;
}
.currency-badge-container {
width : 80px ;
float : right;
}
.badge-left {
float : left;
}
scss コピー
このサンプルが気に入りましたか? 完全な Ignite UI for Angularツールキットにアクセスして、すばやく独自のアプリの作成を開始します。無料でダウンロードできます。
デフォルトで、Grid は独自のロジックを使用してデータ操作を実行します。
これらのタスクをリモートで実行し、Grid で公開される特定の入力とイベントを使用して Grid に結果のデータを供給できます。
リモート仮想化
IgxGrid は、データ チャンクがリモート サービスから要求されるシナリオをサポートし、内部で使用される igxForOf
ディレクティブで実装された動作を公開します。
この機能を使用するには、dataPreLoad
出力にサブスクライブし、取得した引数に基づいて適切な要求を行い、パブリック IgxGrid プロパティ totalItemCount
とサービスの各情報を設定します。
<igx-grid #grid [data ]="remoteData | async" [autoGenerate ]="false"
(dataPreLoad )="processData(false)"
(sortingDone )="processData(true)" >
<igx-column [field ]="'ProductID'" [sortable ]="true" > </igx-column >
<igx-column [field ]="'ProductName'" [sortable ]="true" > </igx-column >
<igx-column [field ]="'UnitPrice'" [dataType ]="'number'" [formatter ]="formatCurrency" [sortable ]="true" > </igx-column >
</igx-grid >
html
public ngAfterViewInit ( ) {
this .grid.isLoading = true ;
this ._remoteService.getData(this .grid.virtualizationState, this .grid.sortingExpressions[0 ], true , (data ) => {
this .grid.totalItemCount = data['@odata.count' ];
this .grid.isLoading = false ;
});
}
public processData (reset ) {
if (this .prevRequest) {
this .prevRequest.unsubscribe();
}
this ._prevRequest = this ._remoteService.getData(this .grid.virtualizationState,
this .grid.sortingExpressions[0 ], reset, () => {
...
this .cdr.detectChanges();
});
}
typescript
データを要求時、startIndex
と chunkSize
プロパティを提供する IForOfState
インターフェイスを使用する必要があります。
最初の chunkSize
は常に 0 で、特定のアプリケーション シナリオに基づいて設定する必要があります。
リモート仮想化のデモ
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { GridRemoteVirtualizationSampleComponent } from "./grid/grid-sample-4/grid-sample-4.component" ;
import {
IgxGridModule,
IgxToastModule,
IgxBadgeModule
} from "igniteui-angular" ;
import { HttpClientModule } from "@angular/common/http" ;
import { RemoteServiceVirt } from "./services/remoteVirtualization.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
GridRemoteVirtualizationSampleComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxToastModule,
IgxBadgeModule,
HttpClientModule
],
providers : [RemoteServiceVirt],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { ChangeDetectorRef, Component, TemplateRef, ViewChild, OnInit, AfterViewInit, OnDestroy } from '@angular/core' ;
import { IgxColumnComponent, IgxGridComponent } from 'igniteui-angular' ;
import { debounceTime } from 'rxjs/operators' ;
import { RemoteServiceVirt } from '../../services/remoteVirtualization.service' ;
@Component ({
providers : [RemoteServiceVirt],
selector : 'app-grid-remote-virtualization-sample' ,
styleUrls : ['grid-sample-4.component.scss' ],
templateUrl : 'grid-sample-4.component.html'
})
export class GridRemoteVirtualizationSampleComponent implements OnInit , AfterViewInit , OnDestroy {
@ViewChild ('grid' , { static : true }) public grid: IgxGridComponent;
@ViewChild ('remoteDataLoadingLarge' , { read : TemplateRef, static : true })
public remoteDataLoadingLargeTemplate: TemplateRef<any >;
@ViewChild ('remoteDataLoadingMedium' , { read : TemplateRef, static : true })
public remoteDataLoadingMediumTemplate: TemplateRef<any >;
@ViewChild ('remoteDataLoadingSmall' , { read : TemplateRef, static : true })
public remoteDataLoadingSmallTemplate: TemplateRef<any >;
public remoteData: any ;
private _columnCellCustomTemplates: Map <IgxColumnComponent, TemplateRef<any >>;
private _prevRequest: any ;
constructor (private _remoteService: RemoteServiceVirt, public cdr: ChangeDetectorRef ) { }
public ngOnInit(): void {
this .remoteData = this ._remoteService.data;
this ._columnCellCustomTemplates = new Map <IgxColumnComponent, TemplateRef<any >>();
}
public ngAfterViewInit ( ) {
this .grid.isLoading = true ;
this ._remoteService.getData(this .grid.virtualizationState, this .grid.sortingExpressions[0 ], true ,
(data ) => {
this .grid.totalItemCount = data['@odata.count' ];
this .grid.isLoading = false ;
}, {
startIndex : this .grid.virtualizationState.startIndex,
chunkSize : 20
});
this .grid.dataPreLoad.pipe().subscribe(() => {
this ._remoteService.getDataFromCache(this .grid.virtualizationState,
this .grid.sortingExpressions[0 ], false , () => {
this .cdr.detectChanges();
});
});
this .grid.dataPreLoad.pipe(debounceTime(500 )).subscribe(() => {
this .processData(false );
});
}
public handlePreLoad ( ) {
this .processData(false );
}
public processData (reset ) {
if (this ._prevRequest) {
this ._prevRequest.unsubscribe();
}
let state;
if (!reset) {
state = {
startIndex : this .grid.virtualizationState.startIndex,
chunkSize : 20
};
}
this ._prevRequest = this ._remoteService.getData(this .grid.virtualizationState,
this .grid.sortingExpressions[0 ], reset, () => {
this .cdr.detectChanges();
}, state);
}
public formatNumber (value: number ) {
return value.toFixed(2 );
}
public formatCurrency (value: number ) {
return '$' + value.toFixed(2 );
}
public ngOnDestroy ( ) {
if (this ._prevRequest) {
this ._prevRequest.unsubscribe();
}
}
public getDataLoadingTemplate(): TemplateRef<any > {
const val = Math .floor(Math .random() * 3 ) + 1 ;
switch (val) {
case 1 : return this .remoteDataLoadingLargeTemplate;
case 2 : return this .remoteDataLoadingMediumTemplate;
case 3 : return this .remoteDataLoadingSmallTemplate;
}
}
}
ts コピー <ng-template #cellTemplate igxCell let-val let-data ="cell.row.data" >
<div *ngIf ="!data.emptyRec; else getDataLoadingTemplate()" >
{{ val }}
</div >
</ng-template >
<div class ="grid__wrapper" >
<ng-template #remoteDataLoadingLarge >
<div class ="remote-data-loading-template-large" > </div >
</ng-template >
<ng-template #remoteDataLoadingMedium >
<div class ="remote-data-loading-template-medium" > </div >
</ng-template >
<ng-template #remoteDataLoadingSmall >
<div class ="remote-data-loading-template-small" > </div >
</ng-template >
<igx-grid [igxPreventDocumentScroll ]="true" #grid [data ]="remoteData | async" [height ]="'500px'" [width ]="'100%'" [autoGenerate ]='false'
(sortingDone )="processData(true)" >
<igx-column [field ]="'ProductID'" [sortable ]="true" [cellTemplate ]="cellTemplate" > </igx-column >
<igx-column [field ]="'ProductName'" [sortable ]="true" [cellTemplate ]="cellTemplate" > </igx-column >
<igx-column [field ]="'UnitPrice'" dataType ="number" [formatter ]="formatCurrency" [sortable ]="true" [cellTemplate ]="cellTemplate" > </igx-column >
<igx-column [field ]="'UnitsInStock'" dataType ="number" [headerClasses ]="'headerAlignSyle'" [sortable ]="true" >
<ng-template igxHeader >
<span class ="cellAlignSyle" > UnitsInStock</span >
</ng-template >
<ng-template igxCell let-val let-cell ="cell" >
<div *ngIf ="!cell.row.data.emptyRec; else getDataLoadingTemplate()" >
<div class ="currency-badge-container" >
<igx-badge *ngIf ="val>50" type ="success" position ="bottom-right" icon ="arrow_upward" class ="badge-left" > </igx-badge >
<igx-badge *ngIf ="val<=50" type ="error" position ="bottom-right" icon ="arrow_downward" class ="error badge-left" > </igx-badge >
<span class ="cellAlignSyle" [class.up ]="val>50" [class.down ]="val<=50" > {{ formatNumber(val) }}</span >
</div >
</div >
</ng-template >
</igx-column >
<igx-column [field ]="'QuantityPerUnit'" [sortable ]="true" [cellTemplate ]="cellTemplate" > </igx-column >
<igx-column [field ]="'ReorderLevel'" dataType ="number" [headerClasses ]="'headerAlignSyle'" [sortable ]="true" >
<ng-template igxHeader >
<span class ="cellAlignSyle" > ReorderLevel</span >
</ng-template >
<ng-template igxCell let-val let-cell ="cell" >
<div *ngIf ="!cell.row.data.emptyRec; else getDataLoadingTemplate()" >
<div class ="currency-badge-container" >
<igx-badge *ngIf ="val>20" type ="success" position ="bottom-right" icon ="arrow_upward" class ="badge-left" > </igx-badge >
<igx-badge *ngIf ="val<=20" type ="error" position ="bottom-right" icon ="arrow_downward" class ="error badge-left" > </igx-badge >
<span class ="cellAlignSyle" [class.up ]="val>0" [class.down ]="val<=0" > {{ formatNumber(val) }}</span >
</div >
</div >
</ng-template >
</igx-column >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
.cellAlignSyle {
text-align : right;
float :right ;
}
.cellAlignSyle > span {
float :right ;
}
.up {
color : green;
}
.down {
color : red;
}
.headerAlignSyle {
text-align : right !important ;
}
.currency-badge-container {
width : 80px ;
float : right;
}
.badge-left {
float : left;
}
.remote-data-loading-template {
animation : content-placeholder-animation .5s infinite;
background-color : lightgray;
height : 15px ;
}
.remote-data-loading-template-medium {
@extend .remote-data-loading-template;
width : 30px ;
}
.remote-data-loading-template-large {
@extend .remote-data-loading-template;
width : 40px ;
}
.remote-data-loading-template-small {
@extend .remote-data-loading-template;
width : 20px ;
}
@keyframes content-placeholder-animation {
0% {
opacity : .75 ;
transform : scaleX(.9 );
}
50% {
opacity : 1 ;
transform : scaleX(1.1 );
}
100% {
opacity : .75 ;
transform : scaleX(.9 );
}
}
scss コピー
無限スクロール
エンドポイントからデータを分割して取得するシナリオの一般的なデザインは、無限スクロールです。データ グリッドの場合、エンドユーザーが一番下までスクロールすることによってトリガーされたロードデータが連続的に増加します。次の段落では、利用可能な API を使用して、IgxGrid
で無限スクロールを簡単に実現する方法を説明します。
無限スクロールを実装するには、データを分割してフェッチする必要があります。すでにフェッチされたデータはローカルに保存し、チャンクの長さおよび数を決定する必要があります。また、グリッドで最後に表示されるデータ行インデックスを追跡する必要があります。このように、startIndex
と chunkSize
プロパティを使用して、ユーザーが上にスクロールして既にフェッチしたデータを表示するか、下にスクロールしてエンドポイントからさらにデータをフェッチする必要があるかを決定できます。
最初に、データの最初のチャンクをフェッチするために ngAfterViewInit
ライフサイクル フックを使用します。totalItemCount
プロパティはグリッドがスクロールバーのサイズを正しく設定できるために重要です。
public ngAfterViewInit ( ) {
this ._remoteService.loadDataForPage(this .page, this .pageSize, (request ) => {
if (request.data) {
this .grid.totalItemCount = this .page * this .pageSize;
this .grid.data = this ._remoteService.getCachedData({startIndex : 0 , chunkSize : 10 });
this .totalItems = request.data['@odata.count' ];
this .totalPageCount = Math .ceil(this .totalItems / this .pageSize);
this .grid.isLoading = false ;
}
});
}
typescript
さらに、dataPreLoad
出力にサブスクライブする必要があります。これにより、グリッドが現在ロードされているものではなく、異なるチャンクを表示しようとするときに必要なデータを提供できます。イベント ハンドラーで、ローカルに既にキャッシュされている新しいデータをフェッチするか、データを返すかを決定する必要があります。
public handlePreLoad ( ) {
const isLastChunk = this .grid.totalItemCount ===
this .grid.virtualizationState.startIndex + this .grid.virtualizationState.chunkSize;
if (isLastChunk) {
if (this .totalPageCount === this .page) {
this .grid.data = this ._remoteService.getCachedData(this .grid.virtualizationState);
return ;
}
this .page++;
this .grid.isLoading = true ;
this ._remoteService.loadDataForPage(this .page, this .pageSize, (request ) => {
if (request.data) {
this .grid.totalItemCount = Math .min(this .page * this .pageSize, this .totalItems);
this .grid.data = this ._remoteService.getCachedData(this .grid.virtualizationState);
this .grid.isLoading = false ;
}
});
} else {
this .grid.data = this ._remoteService.getCachedData(this .grid.virtualizationState);
}
}
typescript
無限スクロールのデモ
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { GridRemoteVirtualizationAddRowSampleComponent } from "./grid/grid-sample-5/grid-sample-5.component" ;
import {
IgxGridModule,
IgxToastModule,
IgxBadgeModule
} from "igniteui-angular" ;
import { HttpClientModule } from "@angular/common/http" ;
import { RemoteService } from "./services/remote.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
GridRemoteVirtualizationAddRowSampleComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxToastModule,
IgxBadgeModule,
HttpClientModule
],
providers : [RemoteService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { AfterViewInit, ChangeDetectorRef, Component, ViewChild, OnInit } from '@angular/core' ;
import { IgxGridComponent } from 'igniteui-angular' ;
import { RemoteService } from '../../services/remote.service' ;
@Component ({
providers : [RemoteService],
selector : 'app-grid-remote-virtualization-sample' ,
styleUrls : ['grid-sample-5.component.scss' ],
templateUrl : 'grid-sample-5.component.html'
})
export class GridRemoteVirtualizationAddRowSampleComponent implements AfterViewInit , OnInit {
@ViewChild ('grid' , { static : true })
public grid: IgxGridComponent;
public remoteData: any ;
private page = 1 ;
private pageSize = 10 ;
private totalPageCount = 0 ;
private totalItems = 0 ;
constructor (private _remoteService: RemoteService, public cdr: ChangeDetectorRef ) { }
public ngOnInit(): void {
this .remoteData = this ._remoteService.data;
}
public ngAfterViewInit ( ) {
this .grid.isLoading = true ;
const dataViewSize = parseInt (this .grid.height, 10 ) / this .grid.rowHeight;
this .pageSize = Math .floor(dataViewSize * 1.5 );
this ._remoteService.loadDataForPage(this .page, this .pageSize, (request ) => {
if (request.data) {
this .grid.data = this ._remoteService.getCachedData({startIndex : 0 , chunkSize : 10 });
this .cdr.detectChanges();
this .grid.verticalScrollContainer.totalItemCount = this .page * this .pageSize;
this .totalItems = request.data['@odata.count' ];
this .totalPageCount = Math .ceil(this .totalItems / this .pageSize);
this .grid.isLoading = false ;
}
});
}
public handlePreLoad ( ) {
const isLastChunk = this .grid.totalItemCount ===
this .grid.virtualizationState.startIndex + this .grid.virtualizationState.chunkSize;
if (isLastChunk) {
if (this .totalPageCount === this .page) {
this .grid.data = this ._remoteService.getCachedData(this .grid.virtualizationState);
return ;
}
this .page++;
this .grid.isLoading = true ;
this ._remoteService.loadDataForPage(this .page, this .pageSize, (request ) => {
if (request.data) {
this .grid.totalItemCount = Math .min(this .page * this .pageSize, this .totalItems);
this .grid.data = this ._remoteService.getCachedData(this .grid.virtualizationState);
this .grid.isLoading = false ;
}
});
} else {
this .grid.data = this ._remoteService.getCachedData(this .grid.virtualizationState);
}
}
public formatNumber (value: number ) {
return value.toFixed(2 );
}
public formatCurrency (value: number ) {
return '$' + value.toFixed(2 );
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid [height ]="'480px'" [width ]="'100%'" [autoGenerate ]='false' (dataPreLoad )="handlePreLoad()" >
<igx-column [field ]="'ProductID'" > </igx-column >
<igx-column [field ]="'ProductName'" > </igx-column >
<igx-column [field ]="'UnitPrice'" dataType ="number" [formatter ]="formatCurrency" > </igx-column >
<igx-column [field ]="'UnitsInStock'" dataType ="number" [headerClasses ]="'headerAlignSyle'" >
<ng-template igxHeader >
<span class ="cellAlignSyle" > UnitsInStock</span >
</ng-template >
<ng-template igxCell let-val >
<div class ="currency-badge-container" >
<igx-badge *ngIf ="val>50" type ="success" position ="bottom-right" icon ="arrow_upward" class ="badge-left" > </igx-badge >
<igx-badge *ngIf ="val<=50" type ="error" position ="bottom-right" icon ="arrow_downward" class ="error badge-left" > </igx-badge >
<span class ="cellAlignSyle" [class.up ]="val>50" [class.down ]="val<=50" > {{ formatNumber(val) }}</span >
</div >
</ng-template >
</igx-column >
<igx-column [field ]="'QuantityPerUnit'" > </igx-column >
<igx-column [field ]="'ReorderLevel'" dataType ="number" [headerClasses ]="'headerAlignSyle'" >
<ng-template igxHeader >
<span class ="cellAlignSyle" > ReorderLevel</span >
</ng-template >
<ng-template igxCell let-val >
<div class ="currency-badge-container" >
<igx-badge *ngIf ="val>20" type ="success" position ="bottom-right" icon ="arrow_upward" class ="badge-left" > </igx-badge >
<igx-badge *ngIf ="val<=20" type ="error" position ="bottom-right" icon ="arrow_downward" class ="error badge-left" > </igx-badge >
<span class ="cellAlignSyle" [class.up ]="val>0" [class.down ]="val<=0" > {{ formatNumber(val) }}</span >
</div >
</ng-template >
</igx-column >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
.cellAlignSyle {
text-align : right;
float :right ;
}
.cellAlignSyle > span {
float :right ;
}
.up {
color : green;
}
.down {
color : red;
}
.headerAlignSyle {
text-align : right !important ;
}
.currency-badge-container {
width : 80px ;
float : right;
}
.badge-left {
float : left;
}
.remote-data-loading-template {
animation : content-placeholder-animation .5s infinite;
background-color : lightgray;
height : 15px ;
}
.remote-data-loading-template-medium {
@extend .remote-data-loading-template;
width : 30px ;
}
.remote-data-loading-template-large {
@extend .remote-data-loading-template;
width : 40px ;
}
.remote-data-loading-template-small {
@extend .remote-data-loading-template;
width : 20px ;
}
@keyframes content-placeholder-animation {
0% {
opacity : .75 ;
transform : scaleX(.9 );
}
50% {
opacity : 1 ;
transform : scaleX(1.1 );
}
100% {
opacity : .75 ;
transform : scaleX(.9 );
}
}
scss コピー
リモート ソート/フィルタリング
リモート ソートとフィルタリングには、取得した引数に基づいて適切な要求を実行するために dataPreLoad
、sortingExpressionsChange
および filteringExpressionsTreeChange
出力にサブスクライブし、サービスから送信される相対する情報とパブリック IgxGrid の totalItemCount
プロパティを設定する必要があります。
また、rxjs debounceTime
関数を使用します。この関数は、特定の期間の経過後、別のソースが出力されない場合にのみ、Observable のソースから値を出力します。この方法では、ユーザーが中断することなく指定された時間が経過した場合にのみ、リモート操作がトリガーされます。
const DEBOUNCE_TIME = 300 ;
...
public ngAfterViewInit ( ) {
...
this .grid.dataPreLoad.pipe(
debounceTime(DEBOUNCE_TIME),
takeUntil(this .destroy$)
).subscribe(() => {
this .processData();
});
this .grid.filteringExpressionsTreeChange.pipe(
debounceTime(DEBOUNCE_TIME),
takeUntil(this .destroy$)
).subscribe(() => {
this .processData(true );
});
this .grid.sortingExpressionsChange.pipe(
debounceTime(DEBOUNCE_TIME),
takeUntil(this .destroy$)
).subscribe(() => {
this .processData();
});
}
typescript
リモート ソートとフィルタリングが提供される場合、グリッドの組み込みのソートとフィルタリングは必要ありません。グリッドの sortStrategy
および filterStrategy
入力をそれぞれのインスタンスの NoopSortingStrategy
および NoopFilteringStrategy
に設定して、無効にできます。
<igx-grid #grid [data ]="remoteData | async" [height ]="'500px'" [width ]="'100%'" [autoGenerate ]='false'
[filterStrategy ]="noopFilterStrategy"
[sortStrategy ]="noopSortStrategy"
[allowFiltering ]="true" >
...
</igx-grid >
html
public noopFilterStrategy = NoopFilteringStrategy.instance();
public noopSortStrategy = NoopSortingStrategy.instance();
typescript
リモー トデータが要求された場合、フィルタリング操作が大文字と小文字を区別します。
リモート ソート/フィルタリングのデモ
このトピックのはじめにあるコードの結果は、デモ で確認できます。
一意の列値ストラテジ
Excel スタイル フィルタリング ダイアログ内のリスト項目は、それぞれの列の一意の値を表します。Grid は、デフォルトでデータソースに基づいてこれらの値を生成します。リモート フィルタリングの場合、グリッドのデータにはサーバーからのすべてのデータが含まれていません。これらの一意の値を手動で提供し、オンデマンドで読み込むために、Grid の uniqueColumnValuesStrategy
入力を利用できます。この入力は、実際には 3 つの引数を提供するメソッドです。
column - フィルタリング式ツリー。各列に基づいて削減されます。
filteringExpressionsTree - フィルタリング式ツリー。各列に基づいて削減されます。
done - サーバーから取得されたときに、新しく生成された列値で呼び出されるコールバック。
開発者は、列 と filteringExpressionsTree 引数によって提供される情報に基づいて、必要な一意の列値を手動で生成し、done コールバックを呼び出すことができます。
<igx-grid #grid1 [data ]="data" [filterMode ]="'excelStyleFilter'" [uniqueColumnValuesStrategy ]="columnValuesStrategy" >
...
</igx-grid >
html
public columnValuesStrategy = (column: ColumnType,
columnExprTree: IFilteringExpressionsTree,
done: (uniqueValues: any []) => void ) => {
this .remoteValuesService.getColumnData(column, columnExprTree, uniqueValues => done(uniqueValues));
}
typescript
一意の列値ストラテジのデモ
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { GridExcelStyleFilteringLoadOnDemandComponent } from "./grid/grid-excel-style-filtering-load-on-demand/grid-excel-style-filtering-load-on-demand.component" ;
import { IgxGridModule } from "igniteui-angular" ;
import { RemoteValuesService } from "./grid/grid-excel-style-filtering-load-on-demand/remoteValues.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
GridExcelStyleFilteringLoadOnDemandComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule
],
providers : [RemoteValuesService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { Component, OnInit } from '@angular/core' ;
import { IFilteringExpressionsTree, IgxColumnComponent } from 'igniteui-angular' ;
import { RemoteValuesService } from './remoteValues.service' ;
@Component ({
selector : 'app-grid-excel-style-filtering-load-on-demand' ,
styleUrls : ['./grid-excel-style-filtering-load-on-demand.component.scss' ],
templateUrl : './grid-excel-style-filtering-load-on-demand.component.html' ,
providers : [RemoteValuesService]
})
export class GridExcelStyleFilteringLoadOnDemandComponent implements OnInit {
public data: any [];
constructor (private remoteValuesService: RemoteValuesService ) { }
public columnValuesStrategy = (column: IgxColumnComponent,
columnExprTree: IFilteringExpressionsTree,
done: (uniqueValues: any []) => void ) => {
this .remoteValuesService.getColumnData(column, columnExprTree, uniqueValues => done(uniqueValues));
};
public ngOnInit ( ) {
this .data = this .remoteValuesService.getRecordsData();
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid1 [data ]="data" [moving ]="true" [height ]="'750px'" [displayDensity ]="'cosy'"
[allowFiltering ]="true" [filterMode ]="'excelStyleFilter'"
[uniqueColumnValuesStrategy ]="columnValuesStrategy" >
<igx-grid-toolbar >
<igx-grid-toolbar-actions >
<igx-grid-toolbar-hiding > </igx-grid-toolbar-hiding >
<igx-grid-toolbar-pinning > </igx-grid-toolbar-pinning >
</igx-grid-toolbar-actions >
</igx-grid-toolbar >
<igx-column [field ]="'ID'" [filterable ]="true" [sortable ]="true" [dataType ]="'string'" > </igx-column >
<igx-column [field ]="'CompanyName'" [filterable ]="true" [sortable ]="true" [dataType ]="'string'" > </igx-column >
<igx-column [field ]="'Employees'" [filterable ]="true" [sortable ]="true" [dataType ]="'number'" > </igx-column >
<igx-column [field ]="'Contract'" [filterable ]="true" [sortable ]="true" [dataType ]="'boolean'" > </igx-column >
<igx-column [field ]="'DateCreated'" [filterable ]="true" [sortable ]="true" [dataType ]="'date'" > </igx-column >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
scss コピー
Excel スタイル フィルタリングのカスタム ロード テンプレートを提供するには、igxExcelStyleLoading
ディレクティブを使用できます。
<igx-grid [data ]="data" [filterMode ]="'excelStyleFilter'" [uniqueColumnValuesStrategy ]="columnValuesStrategy" >
...
<ng-template igxExcelStyleLoading >
Loading ...
</ng-template >
</igx-grid >
html
リモート ページング
はじめにデータ フェッチングを行うサービスを宣言します。デフォルトのページング テンプレートを使用する場合、totalRecords
プロパティを設定する必要があります。それにより、グリッドは合計リモート レコードに基づいて合計ページ番号を計算できます。注: リモート サービスからフェッチ データを実装する必要があります。ページ カウントを計算するためにすべてのデータ項目のカウントをが必要なため、ロジックをサービスに追加する必要があります。
@Injectable ()
export class RemotePagingService {
public remoteData: BehaviorSubject<any []>;
public dataLenght: BehaviorSubject<number > = new BehaviorSubject(0 );
public url = 'https://www.igniteui.com/api/products' ;
constructor (private http: HttpClient ) {
this .remoteData = new BehaviorSubject([]) as any ;
}
public getData(index?: number , perPage?: number ): any {
let qS = '' ;
if (perPage) {
qS = `?$skip=${index} &$top=${perPage} &$count=true` ;
}
this .http
.get(`${this .url + qS} ` ).pipe(
map((data: any ) => data)
).subscribe((data ) => this .remoteData.next(data));
}
public getDataLength(): any {
return this .http.get(this .url).pipe(
map((data: any ) => data.length)
);
}
}
typescript
サービスを宣言した後にコンポーネントを作成する必要があり、Grid コンストラクションとデータ サブスクリプションを処理します。
export class RemotePagingGridSample implements OnInit , AfterViewInit , OnDestroy {
public data: Observable<any []>;
private _dataLengthSubscriber;
constructor (private remoteService: RemoteService ) {}
public ngOnInit ( ) {
this .data = this .remoteService.remoteData.asObservable();
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data ) => {
this .totalCount = data;
this .grid1.isLoading = false ;
});
}
public ngOnDestroy ( ) {
if (this ._dataLengthSubscriber) {
this ._dataLengthSubscriber.unsubscribe();
}
}
}
typescript
これで、独自のカスタム ページング テンプレートを設定するか、igx-paginator
が提供するデフォルトのテンプレートを使用するかを選択できます。まず、デフォルトのページング テンプレートを使用してリモート ページングを設定するために必要なものを見てみましょう。
デフォルト テンプレートのリモート ページング
デフォルトのページング テンプレートを使用する場合、totalRecords
プロパティを設定する必要があります。それにより、グリッドはリモートの合計レコード数に基づいて合計ページ番号を計算できます。リモート ページネーションを実行する場合、グリッドに現在のページのデータのみを渡すため、グリッドは提供されたデータソースのページネーションを試行しません。そのため、pagingMode
プロパティを GridPagingMode.remote に設定する必要があります。リモート サービスからデータをフェッチするために pagingDone
または perPageChange
イベントにサブスクライブする必要があります。イベントが使用されるユース ケースによって異なります。
<igx-grid #grid1 [data ]="data | async" [isLoading ]="isLoading" [pagingMode ]="mode" >
<igx-column field ="ID" > </igx-column >
...
<igx-paginator [(page )]="page" [(perPage )]="perPage" [totalRecords ]="totalCount"
(pagingDone )="paginate($event.current)" >
</igx-paginator >
</igx-grid >
html
public totalCount = 0 ;
public data: Observable<any []>;
public mode = GridPagingMode.remote;
public isLoading = true ;
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
private _dataLengthSubscriber;
public set perPage (val: number ) {
this ._perPage = val;
this .paginate(0 );
}
public ngOnInit ( ) {
this .data = this .remoteService.remoteData.asObservable();
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data: any ) => {
this .totalCount = data;
this .grid1.isLoading = false ;
});
}
public ngAfterViewInit ( ) {
const skip = this .page * this .perPage;
this .remoteService.getData(skip, this .perPage);
}
public paginate (page: number ) {
this .page = page;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
}
typescript
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { RemotePagingDefaultTemplateComponent } from "./grid/grid-remote-paging-defaultTemplate-sample/remote-paging-default-template.component" ;
import { IgxGridModule } from "igniteui-angular" ;
import { RouterModule } from "@angular/router" ;
import { HttpClientModule } from "@angular/common/http" ;
import { RemotePagingService } from "./services/remotePaging.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
RemotePagingDefaultTemplateComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
RouterModule.forRoot([]),
IgxGridModule,
HttpClientModule
],
providers : [RemotePagingService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core' ;
import { GridPagingMode, IgxGridComponent } from 'igniteui-angular' ;
import { Observable } from 'rxjs' ;
import { RemotePagingService } from '../../services/remotePaging.service' ;
@Component ({
encapsulation : ViewEncapsulation.None,
providers : [RemotePagingService],
selector : 'app-remote-paging-default-template' ,
styleUrls : ['./remote-paging-default-template.component.scss' ],
templateUrl : './remote-paging-default-template.component.html'
})
export class RemotePagingDefaultTemplateComponent implements OnInit , AfterViewInit , OnDestroy {
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
@ViewChild ('customPager' , { read : TemplateRef, static : true }) public remotePager: TemplateRef<any >;
public totalCount = 0 ;
public page = 0 ;
public data: Observable<any []>;
public mode = GridPagingMode.Remote;
public isLoading = true ;
private _dataLengthSubscriber;
private _perPage = 10 ;
public get perPage (): number {
return this ._perPage;
}
public set perPage (val: number ) {
this ._perPage = val;
this .paginate(0 );
}
constructor (private remoteService: RemotePagingService ) {
}
public ngOnInit ( ) {
this .data = this .remoteService.remoteData.asObservable();
this .data.subscribe(() => {
this .isLoading = false ;
});
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data ) => {
this .totalCount = data;
});
}
public ngOnDestroy ( ) {
if (this ._dataLengthSubscriber) {
this ._dataLengthSubscriber.unsubscribe();
}
}
public ngAfterViewInit ( ) {
this .grid1.isLoading = true ;
this .remoteService.getData(0 , this .grid1.perPage);
this .remoteService.getDataLength();
}
public pagingDone (page ) {
const skip = page.current * this .grid1.perPage;
this .remoteService.getData(skip, this .grid1.perPage);
}
public paginate (page ) {
this .isLoading = true ;
const skip = page * this .grid1.perPage;
this .remoteService.getData(skip, this .grid1.perPage);
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid1 [data ]="data | async" width ="100%"
height ="580px" [isLoading ]="isLoading" [pagingMode ]="mode" >
<igx-paginator
[(page )]="page"
[(perPage )]="perPage"
[totalRecords ]="totalCount"
(pagingDone )="paginate($event.current)" >
</igx-paginator >
<igx-column field ="ID" > </igx-column >
<igx-column field ="ProductName" > </igx-column >
<igx-column field ="QuantityPerUnit" > </igx-column >
<igx-column field ="SupplierName" > </igx-column >
<igx-column field ="UnitsInStock" > </igx-column >
<igx-column field ="Rating" > </igx-column >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
scss コピー
カスタム igx-paginator-content のリモート ページング
カスタム ページネーター コンテンツを定義するときは、要求されたページのデータのみを取得するようにコンテンツを定義し、選択したページと perPage
項目に応じて正しい skip および top パラメーターをリモート サービスに渡す必要があります。導入された IgxPageSizeSelectorComponent
と IgxPageNavigationComponent
とともに、設定例を簡単にするために <igx-paginator>
を使用します。igx-page-size
はページごとのドロップダウンとラベルを追加し、igx-page-nav
はナビゲーション アクション ボタンとラベルを追加します。
<igx-paginator #paginator
[totalRecords ]="totalCount"
[(page )]="page"
[(perPage )]="perPage"
[selectOptions ]="selectOptions"
(pageChange )="paginate($event)"
(perPageChange )="perPageChange($event)" >
<igx-paginator-content >
<igx-page-size > </igx-page-size >
[This is my custom content]
<igx-page-nav > </igx-page-nav >
</igx-paginator-content >
</igx-paginator >
html
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
private _perPage = 15 ;
private _dataLengthSubscriber: { unsubscribe : () => void ; } | undefined ;
constructor (private remoteService: RemotePagingService ) { }
public ngAfterViewInit ( ) {
this .grid1.isLoading = true ;
this .remoteService.getData(0 , this .perPage);
}
public paginate (page: number ) {
this .page = page;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
}
public perPageChange (perPage: number ) {
const skip = this .page * perPage;
const top = perPage;
this .remoteService.getData(skip, top);
}
typescript
リモート ページングを適切に構成するには、GridPagingMode.Remote
を設定する必要があります。
<igx-grid #grid1 [data ]="data | async" width ="100%" height ="580px" [pagingMode ]="mode" > </igx-grid >
...
public mode = GridPagingMode.Remote;
html
最後の手順は、要件に基づいてページネーターのコンテンツを宣言することです。
<igx-paginator-content >
<igx-page-size > </igx-page-size >
[This is my custom content]
<igx-page-nav > </igx-page-nav >
</igx-paginator-content >
html
上記すべての設定を完了すると以下のような結果になります。
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { RemotePagingGridSampleComponent } from "./grid/grid-remote-paging-sample/remote-paging-sample.component" ;
import {
IgxGridModule,
IgxSelectModule
} from "igniteui-angular" ;
import { RouterModule } from "@angular/router" ;
import { HttpClientModule } from "@angular/common/http" ;
import { RemotePagingService } from "./services/remotePaging.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
RemotePagingGridSampleComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
RouterModule.forRoot([]),
IgxGridModule,
HttpClientModule,
IgxSelectModule
],
providers : [RemotePagingService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core' ;
import { GridPagingMode, IgxGridComponent } from 'igniteui-angular' ;
import { Observable } from 'rxjs' ;
import { RemotePagingService } from '../../services/remotePaging.service' ;
@Component ({
encapsulation : ViewEncapsulation.None,
providers : [RemotePagingService],
selector : 'app-remote-paging-grid-sample' ,
styleUrls : ['./remote-paging-sample.component.scss' ],
templateUrl : './remote-paging-sample.component.html'
})
export class RemotePagingGridSampleComponent implements OnInit , AfterViewInit , OnDestroy {
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
public page = 0 ;
public totalCount = 0 ;
public pages = [];
public data: Observable<any []>;
public selectOptions = [5 , 10 , 15 , 25 , 50 ];
public mode = GridPagingMode.Remote;
private _perPage = 15 ;
private _dataLengthSubscriber;
constructor (private remoteService: RemotePagingService ) { }
public ngOnInit ( ) {
this .data = this .remoteService.remoteData.asObservable();
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data: any ) => {
this .totalCount = data;
this .grid1.isLoading = false ;
});
}
public get perPage (): number {
return this ._perPage;
}
public set perPage (val: number ) {
this ._perPage = val;
this .paginate(0 );
}
public ngOnDestroy ( ) {
if (this ._dataLengthSubscriber) {
this ._dataLengthSubscriber.unsubscribe();
}
}
public ngAfterViewInit ( ) {
this .grid1.isLoading = true ;
this .remoteService.getData(0 , this .perPage);
}
public paginate (page: number ) {
this .page = page;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
}
public perPageChange (perPage: number ) {
const skip = this .page * perPage;
const top = perPage;
this .remoteService.getData(skip, top);
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid1 [data ]="data | async" width ="100%" height ="580px" [pagingMode ]="mode" >
<igx-column field ="ID" > </igx-column >
<igx-column field ="ProductName" > </igx-column >
<igx-column field ="QuantityPerUnit" > </igx-column >
<igx-column field ="SupplierName" > </igx-column >
<igx-column field ="UnitsInStock" > </igx-column >
<igx-column field ="Rating" > </igx-column >
<igx-paginator #paginator
[totalRecords ]="totalCount"
[(page )]="page"
[(perPage )]="perPage"
[selectOptions ]="selectOptions"
[displayDensity ]="grid1.displayDensity"
(pageChange )="paginate($event)"
(perPageChange )="perPageChange($event)" >
<igx-paginator-content >
<igx-page-size > </igx-page-size >
[This is my custom content]
<igx-page-nav > </igx-page-nav >
</igx-paginator-content >
</igx-paginator >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
scss コピー
カスタム ページネーターのリモート ページング
独自のページング動作を定義するために、ページング テンプレートを使用してカスタム ロジックを追加できます。上記を実証するために、リモート ページングの例を拡張する方法を説明します。
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { CustomRemotePagingGridSampleComponent } from "./grid/grid-custom-remote-paging-sample/custom-remote-paging-sample.component" ;
import {
IgxGridModule,
IgxSelectModule
} from "igniteui-angular" ;
import { RouterModule } from "@angular/router" ;
import { HttpClientModule } from "@angular/common/http" ;
import { RemotePagingService } from "./services/remotePaging.service" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
CustomRemotePagingGridSampleComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
RouterModule.forRoot([]),
IgxGridModule,
HttpClientModule,
IgxSelectModule
],
providers : [RemotePagingService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core' ;
import { IgxGridComponent } from 'igniteui-angular' ;
import { Observable } from 'rxjs' ;
import { RemotePagingService } from '../../services/remotePaging.service' ;
@Component ({
encapsulation : ViewEncapsulation.None,
providers : [RemotePagingService],
selector : 'app-custom-remote-paging-grid-sample' ,
styleUrls : ['./custom-remote-paging-sample.component.scss' ],
templateUrl : './custom-remote-paging-sample.component.html'
})
export class CustomRemotePagingGridSampleComponent implements OnInit , AfterViewInit , OnDestroy {
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
public page = 0 ;
public lastPage = false ;
public firstPage = true ;
public totalPages = 1 ;
public totalCount = 0 ;
public pages = [];
public data: Observable<any []>;
private visibleElements = 5 ;
private _perPage = 10 ;
private _dataLengthSubscriber;
constructor (
private remoteService: RemotePagingService ) {
}
public get perPage (): number {
return this ._perPage;
}
public set perPage (val: number ) {
this ._perPage = val;
this .paginate(0 , true );
}
public get shouldShowLastPage () {
return this .pages[this .pages.length - 1 ] !== this .totalPages - 1 ;
}
public get shouldShowFirstPage () {
return this .pages[0 ] !== 0 ;
}
public ngOnInit ( ) {
this .data = this .remoteService.remoteData.asObservable();
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data ) => {
this .totalCount = data;
this .totalPages = Math .ceil(data / this .perPage);
this .buttonDeselection(this .page, this .totalPages);
this .grid1.isLoading = false ;
this .setNumberOfPagingItems(this .page, this .totalPages);
});
}
public ngOnDestroy ( ) {
if (this ._dataLengthSubscriber) {
this ._dataLengthSubscriber.unsubscribe();
}
}
public ngAfterViewInit ( ) {
this .grid1.isLoading = true ;
this .remoteService.getData(0 , this .perPage);
}
public nextPage ( ) {
this .firstPage = false ;
this .page++;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
if (this .page + 1 >= this .totalPages) {
this .lastPage = true ;
}
this .setNumberOfPagingItems(this .page, this .totalPages);
}
public previousPage ( ) {
this .lastPage = false ;
this .page--;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
if (this .page <= 0 ) {
this .firstPage = true ;
}
this .setNumberOfPagingItems(this .page, this .totalPages);
}
public paginate (page: number , recalculate = false ) {
this .page = page;
const skip = this .page * this .perPage;
const top = this .perPage;
if (recalculate) {
this .totalPages = Math .ceil(this .totalCount / this .perPage);
}
this .setNumberOfPagingItems(this .page, this .totalPages);
this .remoteService.getData(skip, top);
this .buttonDeselection(this .page, this .totalPages);
}
public buttonDeselection (page: number , totalPages: number ) {
if (totalPages === 1 ) {
this .lastPage = true ;
this .firstPage = true ;
} else if (page + 1 >= totalPages) {
this .lastPage = true ;
this .firstPage = false ;
} else if (page !== 0 && page !== totalPages) {
this .lastPage = false ;
this .firstPage = false ;
} else {
this .lastPage = false ;
this .firstPage = true ;
}
}
public activePage (page ) {
return page === this .page ? 'activePage' : '' ;
}
public setNumberOfPagingItems (currentPage, totalPages ) {
if (currentPage > this .pages[0 ] && currentPage < this .pages[this .pages.length]) {
return ;
}
if (this .pages.length === 0 ) {
const lastPage = (currentPage + this .visibleElements) <= totalPages ?
currentPage + this .visibleElements : totalPages;
for (let item = 0 ; item < lastPage; item++) {
this .pages.push(item);
}
return ;
}
if (currentPage <= this .pages[0 ]) {
this .pages = [];
let firstPage = currentPage - 1 < 0 ? 0 : currentPage - 1 ;
firstPage = firstPage > totalPages - this .visibleElements ?
totalPages - this .visibleElements : firstPage;
firstPage = firstPage >= 0 ? firstPage : 0 ;
const lastPage = (firstPage + this .visibleElements) <= totalPages ?
firstPage + this .visibleElements : totalPages;
for (let item = firstPage; item < lastPage; item++) {
this .pages.push(item);
}
} else if (currentPage >= this .pages[this .pages.length - 1 ]) {
this .pages = [];
let firstPage = currentPage > totalPages - this .visibleElements ?
totalPages - this .visibleElements : currentPage - 1 ;
firstPage = firstPage >= 0 ? firstPage : 0 ;
const lastPage = (firstPage + this .visibleElements) <= totalPages ?
firstPage + this .visibleElements : totalPages;
for (let item = firstPage; item < lastPage; item++) {
this .pages.push(item);
}
}
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid1 [data ]="data | async" width ="100%" height ="580px" >
<igx-column field ="ID" > </igx-column >
<igx-column field ="ProductName" > </igx-column >
<igx-column field ="QuantityPerUnit" > </igx-column >
<igx-column field ="SupplierName" > </igx-column >
<igx-column field ="UnitsInStock" > </igx-column >
<igx-column field ="Rating" > </igx-column >
<igx-paginator [perPage ]="perPage" >
<igx-paginator-content >
<div class ="fullWidth" >
<div id ="numberPager" class ="fullWidth" >
<button [disabled ]="firstPage" (click )="previousPage()" igxButton ="flat" >
PREV
</button >
<span *ngIf ="shouldShowFirstPage" (click )="paginate(0)" >
<a class ="pageNavLinks" [routerLink ]="[]" > {{1}}</a > <span class ="pageNavLinks" > ...</span >
</span >
<span *ngFor ="let item of pages" (click )="paginate(item)" >
<a class ="pageNavLinks {{activePage(item)}}" [routerLink ]="[]" > {{item + 1}}</a >
</span >
<span *ngIf ="shouldShowLastPage" (click )="paginate(totalPages - 1)" >
<span class ="pageNavLinks" > ...</span > <a class ="pageNavLinks" [routerLink ]="[]" > {{ totalPages
}}</a >
</span >
<button [disabled ]="lastPage" (click )="nextPage()" igxButton ="flat" >
NEXT
</button >
</div >
</div >
</igx-paginator-content >
</igx-paginator >
</igx-grid >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
.pageNavLinks {
text-decoration : none;
font-size : 10.5pt ;
color : #989898 ;
}
.activePage {
font-size : 11.5pt ;
font-weight : bold;
}
.igx-input-group__input {
display : inline;
}
#numberPager {
display : flex;
align-items : center;
justify-content : center;
}
#numberPager span {
margin-left : .5rem ;
}
.fullWidth {
width : 100% ;
}
scss コピー
以下は、独自の next
および previous
ページ操作を実装するために定義したメソッドです。
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
public ngAfterViewInit ( ) {
this .grid1.isLoading = true ;
this .remoteService.getData(0 , this .perPage);
}
public nextPage ( ) {
this .firstPage = false ;
this .page++;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
if (this .page + 1 >= this .totalPages) {
this .lastPage = true ;
}
this .setNumberOfPagingItems(this .page, this .totalPages);
}
public previousPage ( ) {
this .lastPage = false ;
this .page--;
const skip = this .page * this .perPage;
const top = this .perPage;
this .remoteService.getData(skip, top);
if (this .page <= 0 ) {
this .firstPage = true ;
}
this .setNumberOfPagingItems(this .page, this .totalPages);
}
public paginate (page: number , recalculate = false ) {
this .page = page;
const skip = this .page * this .perPage;
const top = this .perPage;
if (recalculate) {
this .totalPages = Math .ceil(this .totalCount / this .perPage);
}
this .setNumberOfPagingItems(this .page, this .totalPages);
this .remoteService.getData(skip, top);
this .buttonDeselection(this .page, this .totalPages);
}
typescript
一括編集のリモート ページング
これまでの例で、リモート データで IgxGrid を設定する方法を説明しました。次に、一括編集のトピック に従ってグリッドのバッチ編集を有効にします。
サンプルを続行する前に、現在のユースケースを明確します。ページネーションを実行すると、グリッドには現在のページのデータのみが含まれます。新しい行を追加すると、(一括編集により) 新しく追加された行はグリッドに含まれる現在のデータと連結されます。したがって、サーバーが指定されたページのデータを返さない場合、グリッドのデータソースは新しく追加された行のみで構成され、グリッドは定義されたページ設定 (page、perPage) に基づいてページを作成します。
public ngOnInit ( ) {
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data ) => {
this .totalCount = data;
this ._recordOnServer = data;
this ._totalPagesOnServer = Math .floor(this .totalCount / this .perPage);
this .grid1.isLoading = false ;
});
}
typescript
このユースケースを適切に処理するには、カスタム ロジックを実装する必要があります。最初に、サーバー上にあるレコードの総数を知る必要があります。サーバーのデータ ページの総数を計算し (this._totalPagesOnServer
を参照)、その値に基づいてカスタム ページネーション ロジックを実装します。
public paginate (page: number ) {
this .grid1.endEdit(true );
if (page > this ._totalPagesOnServer) {
if (this .page !== this ._totalPagesOnServer) {
const skipEl = this ._totalPagesOnServer * this .perPage;
this .remoteService.getData(skipEl, this .perPage);
}
this .page = page - this ._totalPagesOnServer;
this .page = page;
return ;
} else {
this .page = 0 ;
}
this .page = page;
const skip = this .page * this .perPage;
this .remoteService.getData(skip, this .perPage);
}
typescript
paginate メソッドで示されるように、_totalPagesOnServer
値に基づいてカスタム ページネーション ロジックが実行されます。
一括編集のリモート ページングのデモ
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { RemotePagingBatchEditingComponent } from "./grid/grid-batchEditing-remotePaging/batch-editing-remote-paging.component" ;
import {
IgxGridModule,
IgxDialogModule,
IgxButtonModule
} from "igniteui-angular" ;
import { RemotePagingService } from "./services/remotePaging.service" ;
import { HttpClientModule } from "@angular/common/http" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
RemotePagingBatchEditingComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDialogModule,
IgxButtonModule,
HttpClientModule
],
providers : [RemotePagingService],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core' ;
import { GridPagingMode, IgxDialogComponent, IgxGridComponent, Transaction } from 'igniteui-angular' ;
import { Observable } from 'rxjs' ;
import { RemotePagingService } from '../../services/remotePaging.service' ;
@Component ({
encapsulation : ViewEncapsulation.None,
providers : [RemotePagingService],
selector : 'app-remote-paging-batch-editing' ,
styleUrls : ['./batch-editing-remote-paging.component.scss' ],
templateUrl : './batch-editing-remote-paging.component.html'
})
export class RemotePagingBatchEditingComponent implements OnInit , AfterViewInit , OnDestroy {
@ViewChild ('grid1' , { static : true }) public grid1: IgxGridComponent;
@ViewChild (IgxDialogComponent, { static : true }) public dialog: IgxDialogComponent;
public page = 0 ;
public totalCount = 0 ;
public data: Observable<any []>;
public selectOptions = [5 , 10 , 15 , 25 , 50 ];
public transactionsData: Transaction[] = [];
public mode = GridPagingMode.Remote;
private _perPage = 10 ;
private _dataLengthSubscriber;
private _recordOnServer = 0 ;
private _totalPagesOnServer = 0 ;
constructor (private remoteService: RemotePagingService ) {
}
public get perPage (): number {
return this ._perPage;
}
public set perPage (val: number ) {
this ._perPage = val;
this ._totalPagesOnServer = Math .floor(this ._recordOnServer / this .perPage);
this .paginate(0 );
}
public ngOnInit ( ) {
this .data = this .remoteService.remoteData.asObservable();
this ._dataLengthSubscriber = this .remoteService.getDataLength().subscribe((data ) => {
this .totalCount = data;
this ._recordOnServer = data;
this ._totalPagesOnServer = Math .floor(this .totalCount / this .perPage);
this .grid1.isLoading = false ;
});
}
public ngOnDestroy ( ) {
if (this ._dataLengthSubscriber) {
this ._dataLengthSubscriber.unsubscribe();
}
}
public ngAfterViewInit ( ) {
this .grid1.isLoading = true ;
this .remoteService.getData(0 , this .perPage);
}
public paginate (page: number ) {
this .grid1.endEdit(true );
if (page > this ._totalPagesOnServer) {
if (this .page !== this ._totalPagesOnServer) {
const skipEl = this ._totalPagesOnServer * this .perPage;
this .remoteService.getData(skipEl, this .perPage);
}
this .page = page - this ._totalPagesOnServer;
this .page = page;
return ;
} else {
this .page = 0 ;
}
this .page = page;
const skip = this .page * this .perPage;
this .remoteService.getData(skip, this .perPage);
}
public addRow ( ) {
this .totalCount++;
const newID = this .generateRandomInteger(this .totalCount, this .totalCount * 100 );
this .grid1.addRow({
ID : newID, ProductName : 'Product Name' , QuantityPerUnit : 'Quantity per Unit' ,
SupplierName : 'Supplier Name' , UnitsInStock : 1 , Rating : 1
});
}
public deleteRow (rowID ) {
if (!this .grid1.data.some(d => d.ID === rowID)) {
this .totalCount--;
}
this .grid1.deleteRow(rowID);
if (this .grid1.dataView.length === 1 ) {
this .paginate(this .page - 1 );
}
}
public generateRandomInteger (start: number , end: number ) {
return Math .floor(Math .random() * (end - start + 1 )) + start;
}
public undo ( ) {
this .grid1.transactions.undo();
}
public redo ( ) {
this .grid1.transactions.redo();
}
public openCommitDialog ( ) {
this .transactionsData = this .grid1.transactions.getAggregatedChanges(true );
this .dialog.open();
}
public commit ( ) {
this .grid1.transactions.commit(this .grid1.data);
this .dialog.close();
}
public cancel ( ) {
this .dialog.close();
}
public discard ( ) {
this .grid1.transactions.clear();
this .totalCount = this ._recordOnServer;
this ._totalPagesOnServer = Math .floor(this ._recordOnServer / this .perPage);
this .paginate(this ._totalPagesOnServer);
this .dialog.close();
}
public get hasTransactions (): boolean {
return this .grid1.transactions.getAggregatedChanges(false ).length > 0 ;
}
public stateFormatter (value: string ) {
return JSON .stringify(value);
}
public typeFormatter (value: string ) {
return value.toUpperCase();
}
public classFromType(type : string ): string {
return `transaction--${type .toLowerCase()} ` ;
}
}
ts コピー <div class ="grid__wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid1 [batchEditing ]="true" [data ]="data | async" width ="100%" height ="580px" [pagingMode ]="mode"
[rowEditable ]="true" [primaryKey ]="'ID'" >
<igx-paginator #paginator
[totalRecords ]="totalCount"
[page ]="page"
[(perPage )]="perPage"
[selectOptions ]="selectOptions"
[displayDensity ]="grid1.displayDensity"
(pageChange )="paginate($event)" >
</igx-paginator >
<igx-grid-toolbar >
<igx-grid-toolbar-actions >
<button igxButton (click )="addRow()" > Add Row</button >
<button igxButton [disabled ]="!grid1.transactions.canUndo" (click )="undo()" > Undo</button >
<button igxButton [disabled ]="!grid1.transactions.canRedo" (click )="redo()" > Redo</button >
<button igxButton [disabled ]="!hasTransactions" (click )="openCommitDialog()" > Commit</button >
</igx-grid-toolbar-actions >
</igx-grid-toolbar >
<igx-column [pinned ]="true" [filterable ]="false" [editable ]="false" >
<ng-template igxCell let-cell ="cell" let-val >
<button igxButton (click )="deleteRow(cell.id.rowID)" [disabled ]="cell.row.deleted" > Delete</button >
</ng-template >
</igx-column >
<igx-column field ="ID" [editable ]="false" > </igx-column >
<igx-column field ="ProductName" > </igx-column >
<igx-column field ="QuantityPerUnit" > </igx-column >
<igx-column field ="SupplierName" > </igx-column >
<igx-column field ="UnitsInStock" > </igx-column >
<igx-column field ="Rating" > </igx-column >
</igx-grid >
<igx-dialog title ="Submit the following transactions?" >
<igx-grid [igxPreventDocumentScroll ]="true" #dialogGrid [data ]="transactionsData" [rowHeight ]="64" [primaryKey ]="'id'"
width ="600px" height ="300px" [emptyGridMessage ]="'No available transactions'" >
<igx-column field ="id" header ="ID" [dataType ]="'string'" width ="100px" > </igx-column >
<igx-column field ="type" header ="Type" width ="150px" [sortable ]="true" >
</igx-column >
<igx-column field ="newValue" header ="Value" width ="900px" >
<ng-template igxCell let-cell ="cell" let-val >
<span class ="transaction-log" > {{ stateFormatter(val) }}</span >
</ng-template >
</igx-column >
</igx-grid >
<div class ="buttons-wrapper" >
<button igxButton (click )="commit()" > Commit</button >
<button igxButton (click )="discard()" > Discard</button >
<button igxButton (click )="cancel()" > Cancel</button >
</div >
</igx-dialog >
</div >
html コピー .grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
.buttons-row {
display : flex;
flex-direction : row;
justify-content : space-between;
padding : 5px ;
}
.buttons-wrapper {
display : flex;
flex-direction : row;
justify-content : center;
padding : 10px 0 ;
}
.transaction--update , .transaction--delete , .transaction--add {
font-weight : 600 ;
}
.transaction--add {
color : #6b3 ;
}
.transaction--update {
color : #4a71b9 ;
}
.transaction--delete {
color : #ee4920 ;
}
.transaction-log {
word-wrap : none;
}
scss コピー
既知の問題と制限
グリッドに primaryKey
が設定されておらず、リモート データ シナリオが有効になっている場合 (ページング、ソート、フィルタリング、スクロール時に、グリッドに表示されるデータを取得するためのリモート サーバーへのリクエストがトリガーされる場合)、データ要求が完了すると、行は次の状態を失います:
リモート データ シナリオでは、グリッドに primaryKey
が設定されている場合、rowSelectionChanging.oldSelection
イベント引数には、現在データ ビューに含まれていない行の完全な行データ オブジェクトが含まれません。この場合、rowSelectionChanging.oldSelection
オブジェクトには、primaryKey
フィールドである 1 つのプロパティのみが含まれます。現在データ ビューにある残りの行については、rowSelectionChanging.oldSelection
に行データ全体が含まれます。
API リファレンス
その他のリソース
コミュニティに参加して新しいアイデアをご提案ください。