React Grid のリモート データ操作
デフォルトで、IgrGrid
は独自のロジックを使用してデータ操作を実行します。
これらのタスクをリモートで実行し、IgrGrid
で公開される特定の入力とイベントを使用して IgrGrid
に結果のデータを供給できます。
無限スクロール
エンドポイントからデータを分割して取得するシナリオの一般的なデザインは、無限スクロールです。データ グリッドの場合、エンドユーザーが一番下までスクロールすることによってトリガーされたロードデータが連続的に増加します。次の段落では、使用可能な API を使用して IgrGrid
で無限スクロールを簡単に実現する方法について説明します。
無限スクロールを実装するには、データを分割してフェッチする必要があります。すでにフェッチされたデータはローカルに保存し、チャンクの長さおよび数を決定する必要があります。また、グリッドで最後に表示されるデータ行インデックスを追跡する必要があります。このように、StartIndex
と ChunkSize
プロパティを使用して、ユーザーが上にスクロールして既にフェッチしたデータを表示するか、下にスクロールしてエンドポイントからさらにデータをフェッチする必要があるかを決定できます。
最初に、データの最初のチャンクをフェッチします。totalItemCount
プロパティはグリッドがスクロールバーのサイズを正しく設定できるようにするために重要です。
さらに、DataPreLoad
出力にサブスクライブする必要があります。これにより、グリッドが現在ロードされているものではなく、異なるチャンクを表示しようとするときに必要なデータを提供できます。イベント ハンドラーで、ローカルに既にキャッシュされている新しいデータをフェッチするか、データを返すかを決定する必要があります。
無限スクロールのデモ
const DATA_URL: string =
"https://services.odata.org/V4/Northwind/Northwind.svc/Products";
const cachedData = <any>[];
let prevRequestChunk: number = 0;
export async function loadDataForPage(
page: number,
pageSize: number,
callback?: (args: any) => void
) : Promise<void>{
const url = buildDataUrl(page, pageSize);
const response = await fetch(url);
const data = await response.json();
const startIndex = (page - 1) * pageSize;
updateData(data, startIndex);
callback({data});
}
export function getCachedData(virtualizationArgs: {startIndex: number, chunkSize: number}) {
const virtArgsEndIndex = virtualizationArgs.startIndex + virtualizationArgs.chunkSize;
let data = [];
if (virtArgsEndIndex > cachedData.length) {
data = cachedData.slice(cachedData.length - prevRequestChunk + 1);
} else {
data = cachedData.slice(virtualizationArgs.startIndex, virtArgsEndIndex);
prevRequestChunk = virtualizationArgs.chunkSize;
}
return data;
}
function buildDataUrl(page: number, pageSize: number): string {
let baseQueryString = `${DATA_URL}?$count=true&`;
const skip = (page - 1) * pageSize;
const pageQuery = `$skip=${skip}&$top=${pageSize}`;
baseQueryString += pageQuery;
return baseQueryString;
}
function updateData(data: any, startIndex: number) {
for(let i=0; i< data.value.length; i++) {
cachedData[i+startIndex] = data.value[i];
}
}
ts
import React, { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { IgrForOfStateEventArgs, IgrGridModule } from "@infragistics/igniteui-react-grids";
import { IgrGrid, IgrColumn } from "@infragistics/igniteui-react-grids";
import { loadDataForPage, getCachedData } from './NwindService';
import "@infragistics/igniteui-react-grids/grids/combined";
import "@infragistics/igniteui-react-grids/grids/themes/light/bootstrap.css";
const mods: any[] = [
IgrGridModule
];
mods.forEach((m) => m.register());
export default function App() {
let pageSize = 10;
let page = 1;
let totalPageCount = 0;
let totalItems = 0;
const gridRef = useRef<IgrGrid>(null);
useEffect(() => {
gridRef.current.isLoading = true;
const dataViewSize = 9.6;
pageSize = Math.floor(dataViewSize * 1.5);
loadDataForPage(page,pageSize, (request) => {
if (request.data) {
gridRef.current.data = getCachedData({startIndex: 0, chunkSize: 10});
gridRef.current.totalItemCount = page * pageSize;
totalItems = request.data['@odata.count'];
totalPageCount = Math.ceil(totalItems / pageSize);
gridRef.current.isLoading = false;
}
});
}, [])
function handlePreLoad(grid: IgrGrid, e: IgrForOfStateEventArgs) {
const isLastChunk = grid.totalItemCount === e.detail.startIndex + e.detail.chunkSize;
if (isLastChunk) {
if (totalPageCount === page) {
grid.data = getCachedData(e.detail);
return;
}
page++;
grid.isLoading = true;
loadDataForPage(page, pageSize, (request) => {
if (request.data) {
grid.totalItemCount = Math.min(page * pageSize, totalItems);
grid.data = getCachedData(e.detail);
grid.isLoading = false;
}
})
} else {
grid.data = getCachedData(e.detail);
}
}
return (
<>
<div className="container sample">
<div className="container fill" >
<IgrGrid
autoGenerate={false} dataPreLoad={handlePreLoad}
ref={gridRef} height='480px' width='100%'>
<IgrColumn field="ProductID" header="Product ID">
</IgrColumn>
<IgrColumn field="ProductName" header="Product Name">
</IgrColumn>
<IgrColumn field="UnitsInStock" header="Units In Stock" dataType="number">
</IgrColumn>
<IgrColumn field="UnitPrice" header="Units Price" dataType="number">
</IgrColumn>
<IgrColumn field="QuantityPerUnit">
</IgrColumn>
<IgrColumn field="ReorderLevel" data-type="number" header-classes="headerAlignSyle">
</IgrColumn>
</IgrGrid>
</div>
</div>
</>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>);
tsx
.contextmenu {
position: absolute;
width: 180px;
background: white;
display: flex;
cursor: context-menu;
flex-direction: column;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
z-index: 9999;
font-size: 0.75rem;
font-weight: 650;
}
.item {
padding: 10px;
display: flex;
}
.item:hover {
background:rgb(204, 203, 203);
}
.icon {
vertical-align: middle;
margin-bottom: 5px;
margin-right: 5px;
}
.selected-data-area{
overflow-y: auto;
width: 50%;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
margin-left: 10px;
}
.wrapper{
margin: 10px;
display: flex;
justify-content: space-evenly;
}
css
このサンプルが気に入りましたか? 完全な Ignite UI for Reactツールキットにアクセスして、すばやく独自のアプリの作成を開始します。無料でダウンロードできます。
リモート ページング
ページング機能はリモート データで処理することもできます。はじめにデータ フェッチングを行うサービスを宣言します。ページ カウントを計算するためすべてのデータ項目のカウントをが必要なため、ロジックをサービスに追加する必要があります。
const CUSTOMERS_URL = `https:
export class RemoteService {
public static getDataWithPaging(pageIndex?: number, pageSize?: number) {
return fetch(this.buildUrl(CUSTOMERS_URL, pageIndex, pageSize))
.then((result) => result.json());
}
private static buildUrl(baseUrl: string, pageIndex?: number, pageSize?: number) {
let qS = "";
if (baseUrl) {
qS += `${baseUrl}`;
}
if (pageIndex !== undefined) {
qS += `?pageIndex=${pageIndex}`;
if (pageSize !== undefined) {
qS += `&size=${pageSize}`;
}
} else if (pageSize !== undefined) {
qS += `?perPage=${pageSize}`;
}
return `${qS}`;
}
}
tsx
サービスを宣言した後にコンポーネントを作成する必要があり、IgrGrid
コンストラクションとデータ サブスクリプションを処理します。
<IgrGrid
ref={grid}
data={data}
pagingMode={GridPagingMode.Remote}
primaryKey="customerId"
height="600px"
isLoading={isLoading}
>
<IgrPaginator
perPage={perPage}
ref={paginator}
pageChange={onPageNumberChange}
perPageChange={onPageSizeChange}>
</IgrPaginator>
<IgrColumn field="customerId" hidden={true}></IgrColumn>
<IgrColumn field="companyName" header="Company Name"></IgrColumn>
<IgrColumn field="contactName" header="Contact Name"></IgrColumn>
<IgrColumn field="contactTitle" header="Contact Title"></IgrColumn>
<IgrColumn field="address.country" header="Country"></IgrColumn>
<IgrColumn field="address.phone" header="Phone"></IgrColumn>
</IgrGrid>
tsx
次に状態を設定します。
const grid = useRef<IgrGrid>(null);
const paginator = useRef<IgrPaginator>(null);
const [data, setData] = useState([]);
const [page, setPage] = useState(0);
const [perPage, setPerPage] = useState(15);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
loadGridData(page, perPage);
}, [page, perPage]);
tsx
最後に、データを読み込むためのメソッドを設定します。
function loadGridData(pageIndex?: number, pageSize?: number) {
setIsLoading(true);
RemoteService.getDataWithPaging(pageIndex, pageSize)
.then((response: CustomersWithPageResponseModel) => {
setData(response.items);
setIsLoading(false);
paginator.current.totalRecords = response.totalRecordsCount;
})
.catch((error) => {
console.error(error.message);
setData([]);
setIsLoading(false);
})
}
tsx
詳細については、以下の完全なサンプルをご覧ください。
グリッド リモート ページングのデモ
EXAMPLE
CustomersWithPageResponseModel.ts
RemotePagingService.ts
TSX
CSS
const CUSTOMERS_URL = `https://data-northwind.indigo.design/Customers/GetCustomersWithPage`;
export class RemoteService {
public static getDataWithPaging(pageIndex?: number, pageSize?: number) {
return fetch(this.buildUrl(CUSTOMERS_URL, pageIndex, pageSize))
.then((result) => result.json());
}
private static buildUrl(baseUrl: string, pageIndex?: number, pageSize?: number) {
let qS = "";
if (baseUrl) {
qS += `${baseUrl}`;
}
if (pageIndex !== undefined) {
qS += `?pageIndex=${pageIndex}`;
if (pageSize !== undefined) {
qS += `&size=${pageSize}`;
}
} else if (pageSize !== undefined) {
qS += `?perPage=${pageSize}`;
}
return `${qS}`;
}
}
ts
import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { IgrGrid, IgrPaginator, IgrGridModule, GridPagingMode } from "@infragistics/igniteui-react-grids";
import { IgrColumn } from "@infragistics/igniteui-react-grids";
import "@infragistics/igniteui-react-grids/grids/combined";
import "@infragistics/igniteui-react-grids/grids/themes/light/bootstrap.css";
import { RemoteService } from "./RemotePagingService";
import { CustomersWithPageResponseModel } from "./CustomersWithPageResponseModel";
import { IgrNumberEventArgs } from "@infragistics/igniteui-react";
IgrGridModule.register();
export default function App() {
const grid = useRef<IgrGrid>(null);
const paginator = useRef<IgrPaginator>(null);
const [data, setData] = useState([]);
const [page, setPage] = useState(0);
const [perPage, setPerPage] = useState(15);
useEffect(() => {
loadGridData(page, perPage);
}, [page, perPage]);
function loadGridData(pageIndex?: number, pageSize?: number) {
grid.current.isLoading = true;
RemoteService.getDataWithPaging(pageIndex, pageSize)
.then((response: CustomersWithPageResponseModel) => {
setData(response.items);
grid.current.isLoading = false;
paginator.current.totalRecords = response.totalRecordsCount;
})
.catch((error) => {
console.error(error.message);
setData([]);
grid.current.isLoading = false;
})
}
function onPageNumberChange(paginator: IgrPaginator, args: IgrNumberEventArgs) {
setPage(args.detail);
}
function onPageSizeChange(paginator: IgrPaginator, args: IgrNumberEventArgs) {
setPerPage(args.detail);
}
return (
<div className="container sample ig-typography">
<div className="container fill">
<IgrGrid
ref={grid}
data={data}
pagingMode={GridPagingMode.Remote}
primaryKey="customerId"
height="600px"
>
<IgrPaginator
perPage={perPage}
ref={paginator}
pageChange={onPageNumberChange}
perPageChange={onPageSizeChange}>
</IgrPaginator>
<IgrColumn field="customerId" hidden={true}></IgrColumn>
<IgrColumn field="companyName" header="Company Name"></IgrColumn>
<IgrColumn field="contactName" header="Contact Name"></IgrColumn>
<IgrColumn field="contactTitle" header="Contact Title"></IgrColumn>
<IgrColumn field="address.country" header="Country"></IgrColumn>
<IgrColumn field="address.phone" header="Phone"></IgrColumn>
</IgrGrid>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
tsx
最後に、RowIslands の動作を設定します。
既知の問題と制限
- グリッドに
PrimaryKey
が設定されておらず、リモート データ シナリオが有効になっている場合 (ページング、ソート、フィルタリング、スクロール時に、グリッドに表示されるデータを取得するためのリモート サーバーへのリクエストがトリガーされる場合)、データ要求が完了すると、行は次の状態を失います:
API リファレンス
その他のリソース
コミュニティに参加して新しいアイデアをご提案ください。