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 } from 'react' ;
import ReactDOM from 'react-dom/client' ;
import './index.css' ;
import { IgrColumn, IgrForOfStateEventArgs, IgrGrid } from "@infragistics/igniteui-react-grids" ;
import { getCachedData, loadDataForPage } from './NwindService' ;
import "@infragistics/igniteui-react-grids/grids/themes/light/bootstrap.css" ;
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(e: IgrForOfStateEventArgs) {
const isLastChunk =
gridRef.current.totalItemCount ===
e.detail.startIndex + e.detail.chunkSize;
if (isLastChunk) {
if (totalPageCount === page) {
gridRef.current.data = getCachedData({
startIndex: e.detail.startIndex,
chunkSize: e.detail.chunkSize,
});
return ;
}
page++;
gridRef.current.isLoading = true ;
loadDataForPage(page, pageSize, (request) => {
if (request.data) {
gridRef.current.totalItemCount = Math.min(
page * pageSize,
totalItems
);
gridRef.current.data = getCachedData({
startIndex: e.detail.startIndex,
chunkSize: e.detail.chunkSize,
});
gridRef.current.isLoading = false ;
}
});
} else {
gridRef.current.data = getCachedData({
startIndex: e.detail.startIndex,
chunkSize: e.detail.chunkSize,
});
}
}
return (
<>
<div className ="container sample" >
<div className ="container fill" >
<IgrGrid
primaryKey ="ProductID"
autoGenerate ={false}
onDataPreLoad ={handlePreLoad}
ref ={gridRef}
height ="480px"
width ="100%"
>
<IgrColumn
field ="ProductID"
header ="Product ID"
sortable ={true}
editable ={true}
> </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 コピー
원격 페이징
페이징 기능은 원격 데이터로 작동할 수 있습니다. 이를 시연하기 위해 먼저 데이터 가져오기를 담당할 서비스를 선언하겠습니다. 페이지 수를 계산하려면 모든 데이터 항목의 수가 필요합니다. 이 로직은 우리 서비스에 추가될 예정입니다.
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 ="remote"
primaryKey ="customerId"
height ="600px"
isLoading ={isLoading}
>
<IgrPaginator
perPage ={perPage}
ref ={paginator}
onPageChange ={onPageNumberChange}
onPerPageChange ={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
추가 참조는 아래의 전체 샘플을 확인하십시오.
그리드 원격 페이징 데모
export interface CustomersWithPageResponseModel {
items : any [];
totalRecordsCount: number ;
pageSize: number ;
pageNumber: number ;
totalPages: number ;
}
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 { IgrGrid, IgrPaginator } 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" ;
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(args: IgrNumberEventArgs) {
setPage(args.detail);
}
function onPageSizeChange(args: IgrNumberEventArgs) {
setPerPage(args.detail);
}
return (
<div className ="container sample ig-typography" >
<div className ="container fill" >
<IgrGrid
ref ={grid}
data ={data}
pagingMode ={ "remote "}
primaryKey ="customerId"
height ="600px"
>
<IgrPaginator
perPage ={perPage}
ref ={paginator}
onPageChange ={onPageNumberChange}
onPerPageChange ={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 참조
추가 리소스
우리 커뮤니티는 활동적이며 항상 새로운 아이디어를 환영합니다.