[]
서버 측 그룹화는 서버에서 데이터를 그룹화하고 지정된 속성에 대한 집계를 계산할 수 있는 유용한 기능입니다. 또한, 가상화는 데이터 요청을 가상화하여 대규모 데이터셋을 처리하는 강력한 메커니즘을 제공합니다. 이 기능은 모든 데이터를 한 번에 로드하는 것이 비효율적일 때, 대규모 컬렉션을 다룰 때 특히 유용합니다.
RestCollectionView
클래스를 확장하여 GroupRestCollectionView
라는 사용자 정의 클래스를 생성하면 가상화를 통해 그룹화된 데이터를 청크 단위로 로드할 수 있습니다.
서버 측 그룹화: 서버에서 데이터를 그룹화합니다.
서버 측 필터링 및 정렬: 데이터 검색을 최적화하기 위해 서버 측에서 필터링과 정렬을 적용합니다.
가상화: 필요한 데이터 부분만 로드하여 대규모 데이터셋을 효율적으로 처리합니다.
집계 함수: 그룹화가 활성화된 경우 서버 측 집계를 지원합니다.
type=info
이 기능은 FlexGrid, FlexSheet, MultiRow에서 지원됩니다.
서버 측 그룹화 기능을 위해 변수를 초기화하고, 서버에서 데이터를 가져오기 위해 getItems
및 getGroupItems
메서드를 재정의해야 합니다.
import { RestCollectionView } from '@mescius/wijmo.rest';
import {copy,httpRequest,asNumber,PropertyGroupDescription} from "@mescius/wijmo";
export class GroupRestCollectionView extends RestCollectionView {
_url: string = '';
constructor(url, options?) {
super(options);
this._url = url;
this.groupOnServer = true;
this.virtualization = true;
copy(this, options);
}
}
서버에서 받은 데이터는 문자열 형식의 날짜를 포함하고 있으며, 이를 JavaScript의 Date 객체로 변환하려면 JSON Reviver 메서드가 필요합니다.
protected _jsonReviver(key: string, value: any): any {
const _rxDate = /^\d{4}\-\d{2}\-\d{2}T\d{2}\:\d{2}\:\d{2}|\/Date\([\d\-]*?\)/;
if (typeof value === 'string' && _rxDate.test(value)) {
value = value.indexOf('/Date(') == 0 // verbosejson
? new Date(parseInt(value.substr(6)))
: new Date(value);
}
return value;
}
서버에 데이터를 요청하기 전에 요청 파라미터를 준비해야 합니다. 이를 위해 _getReadParams
메서드를 다음과 같은 파라미터로 작성합니다:
필터링: 필터 기준을 OData 형식으로 변환하여 요청에 적용합니다.
정렬: 정렬 설명을 바탕으로 order-by 절을 구성합니다.
그룹화: 그룹화 속성을 기준으로 group-by 절을 구성하고 정렬합니다.
추가 정렬: 그룹화 위에 추가적인 정렬 논리를 적용합니다.
집계: 그룹화가 활성화된 경우 필요에 따라 집계 함수를 포함합니다.
_getReadParams(groupInfo: boolean = false, virtualParams: boolean = true): any {
let gDescs = this.groupDescriptions;
let settings: any = {};
// apply filter
if (this.filterOnServer && this._filterProvider) {
let filter = this._asODataFilter(this._filterProvider);
if (filter.length > 0) {
settings.filterBy = filter;
}
}
// update groupBy
if (this.groupOnServer && gDescs.length > 0) {
// send single request to fetch all groups when virtualization only is true
let _groupBy = [];
let _sortBy = [];
for (let i = 0; i < gDescs.length; i++) {
let _prop = (gDescs[i] as PropertyGroupDescription).propertyName;
_groupBy.push(_prop);
_sortBy.push(`${_prop} ASC`);
}
if (groupInfo)
settings.groupBy = _groupBy;
settings.orderBy = _sortBy.join(',');
}
//update orderBy
if (this.sortDescriptions.length > 0) {
let _sortBy = [];
//check existing sort
if (settings.orderBy) {
_sortBy = settings.orderBy.split(',');
}
for (let i = 0; i < this.sortDescriptions.length; i++) {
let sort = `${this.sortDescriptions[i].property} ${this.sortDescriptions[i].ascending ? 'ASC' : 'DESC'}`;
var _cSrtIdx = _sortBy.findIndex(x => x.indexOf(this.sortDescriptions[i].property) > -1);
if (_cSrtIdx > -1)
_sortBy[_cSrtIdx] = sort; // update existing sort
else
_sortBy.push(sort);// add new sort
}
settings.orderBy = _sortBy.join(',');
}
//
if (virtualParams && this.virtualization) {
settings.skip = this._start;
settings.top = this._fetchSize();
}
// set aggregates property if required (only with groupBy)
if (groupInfo && this.aggregates && this.aggregates.length > 0 && this.groupDescriptions.length > 0) {
settings.aggregates = this.aggregates;
}
return settings;
}
type=info
필터는 _asODataFilter 함수를 사용하여 OData 형식으로 변환됩니다. 만약 데이터 소스가 ODataFormat을 지원하지 않는다면, 필터 쿼리를 데이터 소스에서 허용하는 형식으로 변환하는 사용자 정의 메서드를 만들 수 있습니다.
_asODataFilter
메서드 코드는 RestCollectionView\OData 데모 샘플의 rest-collection-view-odata.js 파일에서 가져올 수 있습니다.
요청 파라미터가 준비되었으므로 이제 서버에 데이터와 집계가 포함된 그룹을 요청할 준비가 되었습니다. 이를 위해 getItems
메서드를 재정의하여 데이터 항목을 가져오고, getGroupItems
메서드를 재정의하여 집계가 포함된 그룹을 가져오게 됩니다.
protected getItems(): Promise<any[]> {
// cancel any pending requests
if (this._pendingReq) {
this._pendingReq.abort();
}
return new Promise<any>(resolve => {
let _settings = this._getReadParams(); // get the items virtually
this._pendingReq = httpRequest(this._url, {
requestHeaders: this.requestHeaders,
data: _settings,
success: async xhr => {
// parse response
let resp = JSON.parse(xhr.responseText, this._jsonReviver);
let _count = asNumber(resp.totalItemCount);
if (_count != this._totalItemCount)
this._totalItemCount = _count;
resolve(resp.items);
},
error: xhr => this._raiseError(xhr.responseText, false),
complete: xhr => { this._pendingReq = null; }// no pending requests
});
});;
}
_pendingRequest: XMLHttpRequest;
//fetch group items
protected getGroupItems(): Promise<any[]> {
// cancel any pending requests
if (this._pendingRequest) {
this._pendingRequest.abort();
}
return new Promise<any>(resolve => {
let _settings = this._getReadParams(true, false);
if (this.groupDescriptions.length > 0) {
httpRequest(this._url, {
requestHeaders: this.requestHeaders,
data: _settings,
success: async xhr => {
// parse response
let re = xhr.responseText;
let resp = JSON.parse(xhr.responseText, this._jsonReviver);
if (resp.totalGroupCount) {
this._totalGroupItemCount = asNumber(resp.totalGroupCount);
}
let _count = asNumber(resp.totalItemCount);
if (_count != this._totalItemCount)
this._totalItemCount = _count;
resolve(resp.groupItems);
},
error: xhr => this._raiseError(xhr.responseText, false),
complete: xhr => this._pendingRequest = null // no pending requests
});
}
});
}
이제 JavaScript 파일에서 RESTCollectionView를 호출하여 FlexGrid 컨트롤의 데이터 소스로 사용할 수 있습니다.:
// Extended RESTCollectionView class
import { GroupRestCollectionView } from './group-rest-collection-view';
import { FlexGrid } from '@mescius/wijmo.grid';
function init(){
let cv = new GroupRestCollectionView(url,{
aggregates: `Sum(actualCost) as actualCost,Sum(quantity) as quantity`, // perform aggregation for groups
groupDescriptions: [
// group description to add groups
new PropertyGroupDescription('productName'),
new PropertyGroupDescription('transactionType')
]
}); // create CollectionView Instance
let grid = new FlexGrid("#virtualGrid", {
autoGenerateColumns: false,
columns: [
{ binding: 'productId', header: 'Product ID', width: '' },
{ binding: 'color', header: 'Color', width: '' },
{ binding: 'modifiedDate', header: 'Modified Date', dataType: 'Date', format: 'd' },
{ binding: 'quantity', header: 'Quantity', dataType: 'Number', format: 'n2' },
{ binding: 'actualCost', header: 'Actual Cost', dataType: 'Number', format: 'n2',aggregate:'Sum'}
],
itemsSource:cv // assign Custom Collection View Instance
});
}
type=info
참고:
이 기능은 그룹 지연 로딩 기능과 함께 사용할 수 없습니다.
페이지네이션은 지원되지 않습니다.
서버 및 클라이언트 코드와 함께 샘플 구현을 확인하려면 Wijmo-Rest-CollectionView-Sample을 참고해주세요.