Angular 트리 그리드 그룹화 기준
비계층적 데이터가 있고 다음을 원하는 경우 그룹화 기준 하나 이상의 열을 입력하고 상위 행을 다음으로 채웁니다. 집계된 값 , 당신은 IgxTreeGridComponent
와 함께 treeGridGrouping
선택기가 있는 파이프 및 UI 구성요소 igx-tree-grid-group-by-area
.
treeGridGrouping
파이프는 제공된 매개변수를 기반으로 데이터를 그룹화하고 결과 계층 구조가 별도의 열에 표시됩니다. 집계가 제공되는 경우 파이프는 생성된 상위 행에 대해 집계된 값을 계산할 수도 있습니다. 다음은 템플릿에서 파이프를 사용하는 방법의 예입니다.
<igx-tree-grid #grid
[data ]="data | treeGridGrouping:groupingExpressions:groupKey:childDataKey:grid:aggregations"
[childDataKey ]="childDataKey"
[sortStrategy ]="sorting" >
html
파이프 인수는 다음과 같습니다.
groupingExpressions - 계층 구조를 생성하는 데 사용되는 필드에 대한 정보와 각 그룹의 정렬 세부 정보가 포함된 IGroupingExpression
개체의 배열입니다.
groupKey - 생성된 계층 열 이름의 문자열 값
childDataKey - 생성된 상위 행의 하위 컬렉션이 저장되는 필드의 문자열 값
Grid - 그룹화에 사용되는 IgxTreeGridComponent
aggregations (선택 사항) - 집계 함수에 대한 정보가 포함된 ITreeGridAggregation
개체의 배열
igx-tree-grid-group-by-area
선택기가 있는 UI 구성 요소는 그룹화에 사용되는 열과 관련된 UI 상호 작용을 처리합니다. 다음은 템플릿에서 구성요소를 사용하는 방법의 예입니다.
<igx-tree-grid-group-by-area
[grid ]='grid'
[(expressions )]='groupingExpressions'
[hideGroupedColumns ]='true' >
</igx-tree-grid-group-by-area >
html
구성요소의 입력은 다음과 같습니다.
Grid - 그룹화에 사용되는 IgxTreeGridComponent
표현식 - 계층 구조를 생성하는 데 사용되는 필드가 포함된 IGroupingExpression
객체의 배열
hideGroupedColumns - 그룹화가 수행된 열을 숨길지 여부를 나타내는 부울 값
dropAreaTemplate - 기본 드롭 영역 템플릿을 재정의하는 데 사용할 수 있는 드롭 영역용 템플릿
dropAreaMessage - 기본 드롭 영역 템플릿에 대한 기본 메시지를 재정의하는 데 사용할 수 있는 문자열
정렬이 올바르게 작동하려면 IgxTreeGridComponent
의 sortStrategy
속성을 IgxGroupedTreeGridSorting
의 인스턴스로 설정해야 합니다.
Angular Tree Grid 그룹화 예제
import { Component, ElementRef, OnInit, OnDestroy, ViewChild, HostBinding, inject } from '@angular/core' ;
import { AbsoluteScrollStrategy, ConnectedPositioningStrategy, DefaultSortingStrategy, HorizontalAlignment, IGroupingExpression, IgxButtonGroupComponent, IgxGroupedTreeGridSorting, IgxOverlayOutletDirective, IgxSliderComponent, IgxTreeGridComponent, ITreeGridAggregation, OverlaySettings, PositionSettings, TreeGridFilteringStrategy, VerticalAlignment, IgxGridToolbarComponent, IgxGridToolbarActionsComponent, IgxGridToolbarPinningComponent, IgxGridToolbarHidingComponent, IgxGridToolbarExporterComponent, IgxTreeGridGroupByAreaComponent, IgxColumnComponent, IgxCellEditorTemplateDirective, IgxSelectComponent, IgxFocusDirective, IgxSelectItemComponent, IgxCellTemplateDirective, IgxIconComponent, IgxTreeGridGroupingPipe } from 'igniteui-angular' ;
import { Contract, REGIONS } from '../data/financialData' ;
import { SignalRService } from '../services/signal-r.service' ;
import { FormsModule } from '@angular/forms' ;
import { AsyncPipe, CurrencyPipe, DatePipe } from '@angular/common' ;
import { IgxPreventDocumentScrollDirective } from '../directives/prevent-scroll.directive' ;
@Component ({
providers : [SignalRService],
selector : 'app-tree-grid-finjs-sample' ,
styleUrls : ['./tree-grid-finjs-sample.component.scss' ],
templateUrl : './tree-grid-finjs-sample.component.html' ,
imports : [IgxSliderComponent, FormsModule, IgxButtonGroupComponent, IgxTreeGridComponent, IgxPreventDocumentScrollDirective, IgxGridToolbarComponent, IgxGridToolbarActionsComponent, IgxGridToolbarPinningComponent, IgxGridToolbarHidingComponent, IgxGridToolbarExporterComponent, IgxTreeGridGroupByAreaComponent, IgxColumnComponent, IgxCellEditorTemplateDirective, IgxSelectComponent, IgxFocusDirective, IgxSelectItemComponent, IgxCellTemplateDirective, IgxIconComponent, IgxOverlayOutletDirective, AsyncPipe, CurrencyPipe, DatePipe, IgxTreeGridGroupingPipe]
})
export class TreeGridFinJSComponent implements OnDestroy , OnInit {
private elRef = inject(ElementRef);
dataService = inject(SignalRService);
@ViewChild ('grid1' , { static : true }) public grid1!: IgxTreeGridComponent;
@ViewChild ('buttonGroup1' , { static : true }) public buttonGroup1!: IgxButtonGroupComponent;
@ViewChild ('slider1' , { static : true }) public volumeSlider!: IgxSliderComponent;
@ViewChild ('slider2' , { static : true }) public intervalSlider!: IgxSliderComponent;
@ViewChild (IgxOverlayOutletDirective, { static : true }) public outlet!: IgxOverlayOutletDirective;
@HostBinding ('class.dark-theme' )
public theme = false ;
public showToolbar = true ;
public selectionMode = 'multiple' ;
public volume = 1000 ;
public frequency = 500 ;
public data$: any ;
public overlaySettings: OverlaySettings = {
modal : false
};
public controls = [
{
disabled : false ,
icon : 'update' ,
label : 'LIVE ALL PRICES' ,
selected : false
},
{
disabled : true ,
icon : 'stop' ,
label : 'Stop' ,
selected : false
}
];
public groupingExpressions: IGroupingExpression[] = [
{ fieldName : 'category' , dir : 2 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'type' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'contract' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() }
];
public aggregations: ITreeGridAggregation[] = [
{
aggregate : (parent: any , data: any [] ) => data.map((r ) => r.change).reduce((ty, u ) => ty + u, 0 ),
field : 'change'
},
{
aggregate : (parent: any , data: any [] ) => data.map((r ) => r.price).reduce((ty, u ) => ty + u, 0 ),
field : 'price'
},
{
aggregate : (parent: any , data: any [] ) => parent.change / (parent.price - parent.change) * 100 ,
field : 'changeP'
}
];
public childDataKey = 'children' ;
public groupColumnKey = 'categories' ;
public sorting = IgxGroupedTreeGridSorting.instance();
public filterStrategy = new TreeGridFilteringStrategy([this .groupColumnKey]);
public items: any [] = [{ field : 'Export native' }, { field : 'Export JS Excel' }];
public _positionSettings: PositionSettings = {
horizontalDirection : HorizontalAlignment.Left,
horizontalStartPoint : HorizontalAlignment.Right,
verticalStartPoint : VerticalAlignment.Bottom
};
public _overlaySettings: OverlaySettings = {
closeOnOutsideClick : true ,
modal : false ,
positionStrategy : new ConnectedPositioningStrategy(this ._positionSettings),
scrollStrategy : new AbsoluteScrollStrategy()
};
public contracts = Contract;
public regions = REGIONS;
public isLoading = true ;
private subscription: any ;
private selectedButton: number ;
private _timer: any ;
constructor ( ) {
this .dataService.startConnection();
this .overlaySettings.outlet = this .outlet;
this .data$ = this .dataService.data;
this .dataService.getData(0 );
this .data$.subscribe((data ) => {
if (data.length !== 0 ) {
this .isLoading = false ;
};
});
}
public ngOnInit(): void {
this .overlaySettings.outlet = this .outlet;
}
public onButtonAction(event: any ): void {
switch (event.index) {
case 0 : {
this .disableOtherButtons(event.index, true );
if (this .dataService.hasRemoteConnection) {
this .dataService.broadcastParams(this .frequency, this .volume, true );
} else {
const currData = this .grid1.filteredSortedData ?? this .grid1.data;
this ._timer = setInterval (() => this .dataService.updateAllPriceValues(currData), this .frequency);
}
break ;
}
case 1 : {
if (this .dataService.hasRemoteConnection) {
this .dataService.stopLiveData();
} else {
this .stopFeed();
}
this .disableOtherButtons(event.index, false );
break ;
}
default : break ;
}
}
updateVolume(): void {
this .dataService.hasRemoteConnection ? this .dataService.broadcastParams(this .frequency, this .volume, false ) :
this .dataService.getData(this .volume);
}
public stopFeed(): void {
if (this ._timer) {
clearInterval (this ._timer);
}
if (this .subscription) {
this .subscription.unsubscribe();
}
}
public formatNumber (value: number ) {
return value ? value.toFixed(2 ) : '' ;
}
public percentage (value: number ) {
return value ? value.toFixed(2 ) + '%' : '' ;
}
public formatCurrency (value: number ) {
return value ? '$' + value.toFixed(3 ) : '' ;
}
public onThemeChanged (event: any ) {
const parentEl = this .parentComponentEl();
if (event.checked && parentEl.classList.contains('main' )) {
parentEl.classList.add('fin-dark-theme' );
} else {
parentEl.classList.remove('fin-dark-theme' );
}
}
public ngOnDestroy ( ) {
this .stopFeed();
}
public toggleToolbar ( ) {
this .showToolbar = !this .showToolbar;
}
private negative = (rowData: any ): boolean => rowData['changeP' ] < 0 ;
private positive = (rowData: any ): boolean => rowData['changeP' ] > 0 ;
private changeNegative = (rowData: any ): boolean => rowData['changeP' ] < 0 && rowData['changeP' ] > -1 ;
private changePositive = (rowData: any ): boolean => rowData['changeP' ] > 0 && rowData['changeP' ] < 1 ;
private strongPositive = (rowData: any ): boolean => rowData['changeP' ] >= 1 ;
private strongNegative = (rowData: any , key : string ): boolean => rowData['changeP' ] <= -1 ;
public trends = {
changeNeg : this .changeNegative,
changePos : this .changePositive,
negative : this .negative,
positive : this .positive,
strongNegative : this .strongNegative,
strongPositive : this .strongPositive
};
public trendsChange = {
changeNeg2 : this .changeNegative,
changePos2 : this .changePositive,
strongNegative2 : this .strongNegative,
strongPositive2 : this .strongPositive
};
private disableOtherButtons (ind: number , disableButtons: boolean ) {
if (this .subscription) {
this .subscription.unsubscribe();
}
this .volumeSlider.disabled = disableButtons;
this .intervalSlider.disabled = disableButtons;
this .selectedButton = ind;
this .buttonGroup1.buttons.forEach((button, index ) => {
if (index === 1 ) { button.disabled = !disableButtons; } else {
button.disabled = disableButtons;
}
});
}
private parentComponentEl ( ) {
return this .elRef.nativeElement.parentElement.parentElement;
}
get buttonSelected (): number {
return this .selectedButton || this .selectedButton === 0 ? this .selectedButton : -1 ;
}
}
ts コピー <div class ="grid__wrapper ig-scrollbar" >
<div class ="controls-wrapper" >
</div >
<div class ="controls-holder" >
<div class ="switches" >
<div class ="finjs-slider control-item" >
<label for ="slider" > Records: {{volume}}</label >
<igx-slider #slider1 id ="slider" [minValue ]="0" [maxValue ]="10000" [(ngModel )]="volume" (dragFinished )="updateVolume()" [step ]="100"
[thumbLabelVisibilityDuration ]="250" [continuous ]="true" > </igx-slider >
</div >
<div class ="finjs-slider control-item" >
<label for ="slider" > Frequency: {{frequency}} ms</label >
<igx-slider #slider2 id ="slider" [minValue ]="100" [maxValue ]="3000" [step ]="10" [(ngModel )]="frequency"
[thumbLabelVisibilityDuration ]="250" [continuous ]="true" > </igx-slider >
</div >
</div >
<div class ="control-item finjs-play-controls" >
<igx-buttongroup class ="finjssample-btn-group" #buttonGroup1 [values ]="controls"
(selected )="onButtonAction($event)" > </igx-buttongroup >
</div >
</div >
<div class ="sample-toolbar" >
@if (buttonSelected === 0) {
<span > Feeding {{volume}} records every {{frequency / 1000}} sec.
~{{volume/5}} records updated.</span >
}
</div >
<igx-tree-grid #grid1 [igxPreventDocumentScroll ]="true"
[data ]="data$ | async | treeGridGrouping:groupingExpressions:groupColumnKey:childDataKey:grid1:aggregations"
[childDataKey ]="childDataKey"
[sortStrategy ]="sorting"
[height ]="'calc(100% - 76px)'"
width ="100%"
[moving ]="true"
[autoGenerate ]="false"
hiddenColumnsText ="Hidden"
[isLoading ]="isLoading"
[allowFiltering ]="true"
[filterMode ]="'excelStyleFilter'"
[filterStrategy ]="filterStrategy" >
@if (showToolbar) {
<igx-grid-toolbar >
<igx-grid-toolbar-actions >
<igx-grid-toolbar-pinning > </igx-grid-toolbar-pinning >
<igx-grid-toolbar-hiding title ="Indicators" > </igx-grid-toolbar-hiding >
<igx-grid-toolbar-exporter [exportCSV ]="false" > </igx-grid-toolbar-exporter >
</igx-grid-toolbar-actions >
</igx-grid-toolbar >
}
<igx-tree-grid-group-by-area
[grid ]="grid1"
[(expressions )]="groupingExpressions"
[hideGroupedColumns ]="true" >
</igx-tree-grid-group-by-area >
<igx-column [field ]="groupColumnKey" [width ]="'180px'" [sortable ]="false" [resizable ]="true"
[disableHiding ]="true" > </igx-column >
<igx-column [field ]="'category'" [width ]="'100px'" [groupable ]="true" [sortable ]="true" [editable ]="true" >
</igx-column >
<igx-column [field ]="'type'" [width ]="'100px'" [groupable ]="true" [sortable ]="true" [editable ]="true" >
</igx-column >
<igx-column [field ]="'contract'" [width ]="'100px'" [groupable ]="true" [sortable ]="true" [editable ]="true" >
</igx-column >
<igx-column [field ]="'settlement'" [width ]="'100px'" [sortable ]="true" > </igx-column >
<igx-column [field ]="'country'" [width ]="'100px'" [groupable ]="true" [sortable ]="true" [editable ]="true" >
</igx-column >
<igx-column [field ]="'region'" [width ]="'110px'" [groupable ]="true" [sortable ]="true" [editable ]="true" >
<ng-template igxCellEditor let-cell ="cell" let-value >
<igx-select [overlaySettings ]="overlaySettings" [placeholder ]="value" [(ngModel )]="cell.editValue" [igxFocus ]="true" >
@for (r of regions; track r) {
<igx-select-item [value ]="r.Name" > {{ r.Name }}</igx-select-item >
}
</igx-select >
</ng-template >
</igx-column >
<igx-column [field ]="'lastUpdated'" [width ]="'120px'" [editable ]="true" header ="Last Update" dataType ="date" >
<ng-template igxCell let-cell ="cell" >
{{ cell.value | date }}
</ng-template >
</igx-column >
<igx-column [field ]="'openPrice'" [width ]="'120px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'price'" [width ]="'130px'" dataType ="number" [cellClasses ]="trends"
[sortable ]="true" [disableHiding ]="true" >
<ng-template igxCell let-cell ="cell" >
<div class ="finjs-icons" >
<span > {{cell.value | currency:'USD':'symbol':'1.4-4'}}</span >
@if (trends.positive(cell.row.data)) {
<igx-icon > trending_up</igx-icon >
}
@if (trends.negative(cell.row.data)) {
<igx-icon > trending_down</igx-icon >
}
</div >
</ng-template >
</igx-column >
<igx-column [field ]="'change'" [width ]="'120px'" dataType ="number" [headerClasses ]="'headerAlignSyle'"
[sortable ]="true" [cellClasses ]="trendsChange" [formatter ]="formatNumber" >
</igx-column >
<igx-column [field ]="'changeP'" [width ]="'110px'" dataType ="number" [formatter ]="percentage"
[sortable ]="true" [cellClasses ]="trendsChange" >
</igx-column >
<igx-column [field ]="'buy'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'sell'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'spread'" [width ]="'110px'" dataType ="number" [formatter ]="formatNumber" >
</igx-column >
<igx-column [field ]="'volume'" [width ]="'110px'" dataType ="number" [formatter ]="formatNumber"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'highD'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'lowD'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'highY'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'lowY'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
<igx-column [field ]="'startY'" [width ]="'110px'" dataType ="number" [formatter ]="formatCurrency"
[sortable ]="true" > </igx-column >
</igx-tree-grid >
</div >
<div igxOverlayOutlet #outlet ="overlay-outlet" >
</div >
html コピー @use '../../variables' as *;
:host ::ng-deep {
.fin-dark-theme {
.finjs-slider,
.sample-toolbar,
.group-drop-area {
color: contrast-color(null, 'gray' , 900 );
}
.group-drop-area {
background : color(null, 'surface' , 500 );
}
}
.finjs-icons {
display : flex;
align-items : center;
igx-icon {
font-size : rem(16px );
width : rem(16px );
height : rem(16px );
margin -inline-start: rem(4px );
}
}
.igx-grid__grouparea {
max-height : 100% ;
height : auto;
}
.changePos ,
.changeNeg ,
.strongPositive ,
.strongNegative {
color : contrast-color(null, 'gray' , 900 ) !important ;
.igx-grid__td-text {
padding : rem(2px ) rem(5px );
}
}
.positive {
color : color(null, 'success' , 500 ) !important ;
}
.positive .strongPositive {
.igx-grid__td-text {
color : color(null, 'success' , 500 , .8 ) !important ;
}
}
.negative {
color : color(null, 'error' , 500 ) !important ;
}
.negative .strongNegative {
.igx-grid__td-text {
color : color(null, 'error' , 500 , .8 ) !important ;
}
}
.changePos {
.igx-grid__td-text {
background : color(null, 'success' , 500 , .5 );
}
}
.changePos1 {
background : color(null, 'success' , 500 , .5 );
color : contrast-color(null, 'gray' , 900 );
}
.changePos2 {
.igx-grid__td-text {
border -inline-end: rem(4px ) solid color(null, 'success' , 500 , .5 );
padding -inline-end: rem(4px );
}
}
.changeNeg {
.igx-grid__td-text {
background : color(null, 'error' , 500 , .5 );
}
}
.changeNeg1 {
background : color(null, 'error' , 500 , .5 );
color : contrast-color(null, 'gray' , 900 );
}
.changeNeg2 {
.igx-grid__td-text {
border-right : rem(4px ) solid color(null, 'error' , 500 , .5 );
padding-right : rem(4px );
}
}
.igx-grid__td--selected .changePos1 ,
.igx-grid__td--selected .changePos2 ,
.igx-grid__td--selected .changePos {
background-color : color(null, 'success' , 500 , .5 ) !important ;
.finjs-icons ,
.igx-grid__td-text {
color : contrast-color(null, 'gray' , 900 );
}
}
.igx-grid__td--selected .changeNeg1 ,
.igx-grid__td--selected .changeNeg2 ,
.igx-grid__td--selected .changeNeg {
background-color : color(null, 'error' , 500 , .5 ) !important ;
.finjs-icons ,
.igx-grid__td-text {
color : contrast-color(null, 'gray' , 900 );
}
}
.strongPositive {
.igx-grid__td-text {
background : color(null, 'success' , 500 );
}
}
.strongPositive1 {
background : color(null, 'success' , 500 );
color : contrast-color(null, 'gray' , 900 );
}
.strongPositive2 {
.igx-grid__td-text {
border-right : rem(4px ) solid color(null, 'success' , 500 );
padding-right : rem(4px );
}
}
.strongNegative {
.igx-grid__td-text {
background : color(null, 'error' , 500 );
color : contrast-color(null, 'gray' , 900 );
}
}
.strongNegative1 {
background : color(null, 'error' , 500 );
color : contrast-color(null, 'gray' , 900 );
}
.strongNegative2 {
.igx-grid__td-text {
border-right : rem(4px ) solid color(null, 'error' , 500 );
padding-right : rem(4px );
}
}
.igx-grid__td--selected .strongPositive1 ,
.igx-grid__td--selected .strongPositive2 ,
.igx-grid__td--selected .strongPositive {
background-color : color(null, 'success' , 500 ) !important ;
.finjs-icons ,
.igx-grid__td-text {
color : contrast-color(null, 'gray' , 900 );
}
}
.igx-grid__td--selected .strongNegative1 ,
.igx-grid__td--selected .strongNegative2 ,
.igx-grid__td--selected .strongNegative {
background-color : color(null, 'error' , 500 ) !important ;
.finjs-icons ,
.igx-grid__td-text {
color : contrast-color(null, 'gray' , 900 );
}
}
.controls-holder {
display : flex;
justify-content : space-between;
align-items : center;
flex-wrap : wrap;
width : 100% ;
padding -block-end: rem(4px );
}
.switches {
display : flex;
justify-content : space-between;
align-items : center;
flex : 1 0 0% ;
padding-right : rem(20px );
font-size : .9rem ;
}
.control-item {
padding-right : rem(20px );
}
.igx-slider ,
.igx-slider--disabled {
height : rem(24px );
}
.finjs-slider {
width : 40% ;
min-width : rem(145px );
}
.finjs-play-controls {
width : 45% ;
min-width : rem(620px );
margin -block-start: rem(10px );
}
.sample-toolbar {
height : rem(20px );
font-size : .8rem ;
line-height : rem(20px );
margin -block-start: rem(11px );
}
.igx-grid__outlet span ,
.igx-excel-filter span ,
.igx-excel-filter header ,
.igx-excel-filter input {
font-size : 0.8125rem ;
}
.igx-button--icon {
width : 2rem ;
height : 2rem ;
}
}
.grid__wrapper {
position : relative;
width : 100% ;
height : 100% ;
inset-block-start: 0 ;
inset-inline-start: 0 ;
padding : rem(15px );
display : flex;
flex-direction : column;
igx-tree-grid {
--ig-size: var(--ig-size-small);
}
}
igx-grid {
flex : 1 0 0% ;
}
.controls-wrapper {
display : flex;
justify-content : center;
position : relative;
}
.grid-toolbar-title {
max-width : none;
}
scss コピー
이 샘플이 마음에 드시나요? 전체 Ignite UI for Angular 툴킷에 액세스하고 몇 분 안에 나만의 앱을 구축해 보세요. 무료로 다운로드하세요.
구현
이 샘플에서는 그룹화를 위해 treeGridGrouping
파이프와 igx-tree-grid-group-by-area
선택기가 있는 UI 구성 요소를 사용하고 있습니다. 데이터는 "카테고리" , "유형" 및 "계약" 필드별로 그룹화됩니다. 결과 계층 구조는 새로 생성된 "범주" 열에 표시됩니다. 또한 파이프는 "price" , "change" 및 "changeP" 열에 대해 생성된 상위 행의 집계된 값을 계산합니다.
<igx-tree-grid #grid1
[data ]="data$ | async | treeGridGrouping:groupingExpressions:groupColumnKey:childDataKey:grid1:aggregations"
[childDataKey ]="childDataKey"
[sortStrategy ]="sorting" >
<igx-tree-grid-group-by-area
[grid ]="grid1"
[(expressions )]="groupingExpressions"
[hideGroupedColumns ]="true" >
</igx-tree-grid-group-by-area >
<igx-column [field ]="groupColumnKey" > </igx-column >
html
여기에서 그룹화 표현식과 집계가 정의되는 방법을 확인할 수 있습니다.
public groupingExpressions: IGroupingExpression[] = [
{ fieldName : 'category' , dir : 2 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'type' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'contract' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() }
];
public aggregations: ITreeGridAggregation[] = [
{
aggregate : (parent: any , data: any [] ) => data.map((r ) => r.change).reduce((ty, u ) => ty + u, 0 ),
field : 'change'
},
{
aggregate : (parent: any , data: any [] ) => data.map((r ) => r.price).reduce((ty, u ) => ty + u, 0 ),
field : 'price'
},
{
aggregate : (parent: any , data: any [] ) => parent.change / (parent.price - parent.change) * 100 ,
field : 'changeP'
}
];
public childDataKey = 'children' ;
public groupColumnKey = 'categories' ;
public sorting = IgxGroupedTreeGridSorting.instance();
typescript
로드 온 디맨드 예제에 따른 Angular 트리 그리드 그룹
import { Component, Input, OnInit, ViewChild } from '@angular/core' ;
import { DefaultSortingStrategy, IGroupingExpression, IgxTreeGridComponent, IgxTreeGridGroupByAreaComponent, IgxColumnComponent } from 'igniteui-angular' ;
import { TreeGridGroupingLoadOnDemandService, TreeGridGroupingParameters } from './remoteService' ;
import { IgxPreventDocumentScrollDirective } from '../../directives/prevent-scroll.directive' ;
@Component ({
selector : 'app-tree-grid-group-by-load-on-demand-sample' ,
styleUrls : ['./tree-grid-group-by-load-on-demand-sample.component.scss' ],
templateUrl : './tree-grid-group-by-load-on-demand-sample.component.html' ,
imports : [IgxTreeGridComponent, IgxPreventDocumentScrollDirective, IgxTreeGridGroupByAreaComponent, IgxColumnComponent]
})
export class TreeGridGroupByLoadOnDemandComponent implements OnInit {
@ViewChild ('treeGrid' , { static : true }) public treeGrid: IgxTreeGridComponent;
@Input ()
public groupingExpressions: IGroupingExpression[] = [
{ fieldName : 'ShipCountry' , dir : 2 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'ShipCity' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'Discontinued' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() }
];
public primaryKey = 'id' ;
public foreignKey = 'parentId' ;
public hasChildrenKey = 'children' ;
public groupColumnKey = '' ;
public data = [];
private dataService = new TreeGridGroupingLoadOnDemandService();
public ngOnInit ( ) {
this .reloadData();
}
public loadChildren = (parentID: any , done: (children: any []) => void ) => {
const groupingParameters = this .assembleGroupingParameters();
this .dataService.getData(parentID, this .hasChildrenKey, groupingParameters, (children ) => done(children));
};
public onExpressionsChange (event: IGroupingExpression[] ) {
this .reloadData();
}
private reloadData ( ) {
this .treeGrid.isLoading = true ;
this .treeGrid.expansionStates.clear();
const groupingParameters = this .assembleGroupingParameters();
this .dataService.getData(null , this .hasChildrenKey, groupingParameters, (children ) => {
this .data = children;
this .treeGrid.isLoading = false ;
this .treeGrid.reflow();
});
}
private assembleGroupingParameters(): TreeGridGroupingParameters {
const groupingParameters: TreeGridGroupingParameters = {
groupingExpressions : this .groupingExpressions,
groupKey : this .groupColumnKey,
primaryKey : this .primaryKey,
foreignKey : this .foreignKey
};
return groupingParameters;
}
}
ts コピー <div class ="grid__wrapper" >
<igx-tree-grid #treeGrid [igxPreventDocumentScroll ]="true"
[data ]="data"
[loadChildrenOnDemand ]="loadChildren"
[primaryKey ]="primaryKey"
[foreignKey ]="foreignKey"
[hasChildrenKey ]="hasChildrenKey"
width ="100%"
height ="800px"
[autoGenerate ]="false"
hiddenColumnsText ="Hidden" >
<igx-tree-grid-group-by-area
[grid ]="treeGrid"
[(expressions )]="groupingExpressions"
(expressionsChange )="onExpressionsChange($event)"
[hideGroupedColumns ]="true" >
</igx-tree-grid-group-by-area >
<igx-column [field ]="groupColumnKey" [width ]="'180px'" [resizable ]="true" [disableHiding ]="true" > </igx-column >
<igx-column field ="OrderID" header ="Order ID" >
</igx-column >
<igx-column field ="ShipCountry" header ="Ship Country" width ="200px" [groupable ]="true" [hidden ]="true" >
</igx-column >
<igx-column field ="OrderDate" header ="Order Date" width ="200px" dataType ="date" >
</igx-column >
<igx-column field ="PostalCode" header ="Postal Code" width ="200px" [groupable ]="true" >
</igx-column >
<igx-column field ="Discontinued" header ="Discontinued" width ="200px" [groupable ]="true" [hidden ]="true" >
</igx-column >
<igx-column field ="ShipName" header ="Ship Name" width ="250px" [groupable ]="true" >
</igx-column >
<igx-column field ="ShipCity" header ="Ship City" width ="250px" [groupable ]="true" [hidden ]="true" >
</igx-column >
<igx-column field ="ShipperName" header ="Shipper Name" width ="250px" [groupable ]="true" >
</igx-column >
<igx-column field ="Salesperson" header ="Salesperson" width ="250px" [groupable ]="true" >
</igx-column >
<igx-column field ="UnitPrice" header ="Unit Price" width ="150px" dataType ="currency" >
</igx-column >
<igx-column field ="Quantity" header ="Quantity" width ="150px" dataType ="number" >
</igx-column >
</igx-tree-grid >
</div >
html コピー :host ::ng-deep {
.igx-button--icon {
width: 2rem ;
height : 2rem ;
}
}
.grid__wrapper {
--ig-size: var(--ig-size-small);
position : relative;
width : 100% ;
height : 100% ;
top : 0 ;
left : 0 ;
padding : 15px ;
display : flex;
flex-direction : column;
}
igx-grid {
flex : 1 0 0% ;
}
.grid-toolbar-title {
max-width : none;
}
scss コピー
구현
이 샘플에서는 데이터가 부분적으로 로드됩니다. 처음에는 최상위 카테고리만 표시되고, 상위 행이 확장되면 하위 데이터가 제공됩니다. 이 접근 방식에 대한 자세한 내용은 요청 시 트리 그리드 로드 항목을 참조하세요. 데이터는 "ShipCountry" , "ShipCity" 및 "Discontinued" 필드로 그룹화되고 결과 계층 구조는 별도의 열에 표시됩니다. 그룹화는 원격 서비스에서 수행됩니다. 즉, 데이터가 수정되고 계층 보기에 최종 데이터를 표시하는 데 사용되는 해당 하위 및 상위 키가 할당됩니다. 이 서비스의 작동 방식에 대한 자세한 내용은 remoteService.ts
파일의 TreeGridGroupingLoadOnDemandService
클래스를 참조하세요.
다음은 요청 시 로드를 사용하는 방법의 예입니다.
<igx-tree-grid #treeGrid
[data ]="data"
[loadChildrenOnDemand ]="loadChildren"
[primaryKey ]="primaryKey"
[foreignKey ]="foreignKey"
[hasChildrenKey ]="hasChildrenKey" >
<igx-tree-grid-group-by-area
[grid ]="treeGrid"
[(expressions )]="groupingExpressions"
(expressionsChange )="onExpressionsChange($event)"
[hideGroupedColumns ]="true" >
</igx-tree-grid-group-by-area >
<igx-column [field ]="groupColumnKey" > </igx-column >
html
사용자가 행을 확장할 때 하위 행을 로드하기 위해 트리 그리드는 콜백 입력 속성 loadChildrenOnDemand
제공합니다. 하위 데이터는 서버에서 검색되고 그룹화 매개변수에 따라 요청된 상위 행에 할당됩니다.
public groupingExpressions: IGroupingExpression[] = [
{ fieldName : 'ShipCountry' , dir : 2 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'ShipCity' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() },
{ fieldName : 'Discontinued' , dir : 1 , ignoreCase : true , strategy : DefaultSortingStrategy.instance() }
];
public primaryKey = 'id' ;
public foreignKey = 'parentId' ;
public hasChildrenKey = 'children' ;
public groupColumnKey = '' ;
private dataService = new TreeGridGroupingLoadOnDemandService();
public ngOnInit ( ) {
this .reloadData();
}
public loadChildren = (parentID: any , done: (children: any []) => void ) => {
const groupingParameters = this .assembleGroupingParameters();
this .dataService.getData(parentID, this .hasChildrenKey, groupingParameters, (children ) => done(children));
};
private reloadData ( ) {
this .treeGrid.isLoading = true ;
this .treeGrid.expansionStates.clear();
const groupingParameters = this .assembleGroupingParameters();
this .dataService.getData(null , this .hasChildrenKey, groupingParameters, (children ) => {
this .data = children;
this .treeGrid.isLoading = false ;
this .treeGrid.reflow();
});
}
typescript
API 참조
추가 리소스
우리 커뮤니티는 활동적이며 항상 새로운 아이디어를 환영합니다.