계층적 그리드 로드 온디맨드

    The Ignite UI for Angular IgxHierarchicalGrid allows fast rendering by requesting the minimum amount of data to be retrieved from the server so that the user can see the result in view and interact with the visible data as quickly as possible. Initially only the root grid’s data is retrieved and rendered, only after the user expands a row containing a child grid, he will receive the data for that particular child grid. This mechanism, also known as Load on Demand, can be easily configured to work with any remote data.

    이 항목에서는 Northwind WebAPI에서 데이터를 요청하여 요청 시 로드를 구성하는 방법을 보여 줍니다. 다음은 작업 데모이며 나중에 단계별로 살펴보고 만드는 과정을 설명합니다.

    Angular Hierarchical Grid Load On Demand Example

    Hierarchical Grid Setup

    Let's setup our hierarchical grid. First we will define our hierarchical grid template with the levels of hierarchy that we expect to have. We know that our root grid primaryKey for the customers is their customerId, for their orders on the first level - orderId and respectively for order details - productId. Knowing each database table and their keys allows us to define our initial template:

    <igx-hierarchical-grid #hGrid [primaryKey]="'customerId'" [autoGenerate]="true" [height]="'600px'" [width]="'100%'">
        <igx-row-island [key]="'Orders'" [primaryKey]="'orderId'" [autoGenerate]="true">
            <igx-row-island [key]="'Details'" [primaryKey]="'productId'" [autoGenerate]="true">
            </igx-row-island>
        </igx-row-island>
    </igx-hierarchical-grid>
    

    We will easily set the data of the root grid after getting its data from the endpoint in our code later, since we can use the #hGrid reference. Setting the data for any child that has been expanded is a bit different.

    When a row is expanded for the first time, a new child IgxHierarchicalGrid is rendered for it and we need to get the reference for the newly created grid to set its data. That is why each IgxRowIsland component provides the gridCreated event that is fired when a new child grid is created for that specific row island. We can use that to get the reference we need for the new grid, request its data from the endpoint, and apply it.

    엔드포인트에는 행 아일랜드의 키, 부모 행의 기본 키 및 고유 식별자만 필요하기 때문에 모든 행 아일랜드에 대해 하나의 방법을 사용할 수 있습니다. 이 모든 정보는 이벤트 인수에서 직접 액세스할 수 있습니다.

    로딩 표시 설정

    Now let's improve the user experience by informing the user that the data is still loading so he doesn't have to look at an empty grid in the meantime. That's why the IgxHierarchicalGrid supports a loading indicator that can be displayed while the grid is empty.

    We display a loading indicator by setting the isLoading property to true while there is no data. We need to set it initially for the root grid and also when creating new child grids, until their data is loaded. We could always set it to true in our template, but we want to hide it (by setting it to false) and display that the grid has no data if the service returns an empty array.

    Finally, let's turn the autoGenerate property off and define the columns collection in the markup.

    The template file hierarchical-grid-lod.component.html, after all changes added, would look like this:

        <igx-hierarchical-grid #hGrid [data]="remoteData" [isLoading]="true" [primaryKey]="'customerId'" [autoGenerate]="false" [height]="'580px'" [width]="'100%'" [igxPreventDocumentScroll]="true" [allowAdvancedFiltering]="true" [schema]="schema" (advancedFilteringExpressionsTreeChange)="refreshRootGridData()">
                <igx-grid-toolbar></igx-grid-toolbar>
    
                <igx-column field="customerId" [dataType]="'string'"></igx-column>
                <igx-column field="companyName" [dataType]="'string'"></igx-column>
                <igx-column field="contactName" [dataType]="'string'"></igx-column>
                <igx-column field="contactTitle" [dataType]="'string'"></igx-column>
    
            <igx-row-island #rowIsland1 [key]="'Orders'" [primaryKey]="'orderId'" [autoGenerate]="false" (gridCreated)="gridCreated($event)">
                <igx-column field="orderId"></igx-column>
                <igx-column field="customerId"></igx-column>
                <igx-column field="shipVia"></igx-column>
                <igx-column field="freight"></igx-column>
    
                <igx-row-island #rowIsland2 [key]="'Details'" [primaryKey]="'orderId'" [autoGenerate]="false" (gridCreated)="gridCreated($event)">
                    <igx-column field="orderId"></igx-column>
                    <igx-column field="productId"></igx-column>
                    <igx-column field="unitPrice"></igx-column>
                    <igx-column field="quantity"></igx-column>
                    <igx-column field="discount"></igx-column>
                </igx-row-island>
            </igx-row-island>
        </igx-hierarchical-grid>
    

    Advanced filtering

    In order to use Advanced Filtering in the IgxHierarchicalGrid with load on demand, you need to set the schema property of the grid to an entity with hierarchical structure, specifying child entities and fields with their data types. This ensures that filtering expressions with nested queries can be created even before any child grid data is loaded and that the grid can correctly interpret and apply these filters to the data.

    우리의 경우 올바른 계층 구조는 다음과 같습니다.

    public schema: EntityType[] = [
        {
            name: 'Customers',
            fields: [
                { field: 'customerId', dataType: 'string' },
                { field: 'companyName', dataType: 'string' },
                { field: 'contactName', dataType: 'string' },
                { field: 'contactTitle', dataType: 'string' }
            ],
            childEntities: [
                {
                    name: 'Orders',
                    fields: [
                        { field: 'customerId', dataType: 'string' },
                        { field: 'orderId', dataType: 'number' },
                        { field: 'employeeId', dataType: 'number' },
                        { field: 'shipVia', dataType: 'string' },
                        { field: 'freight', dataType: 'number' }
                    ],
                    childEntities: [
                        {
                            name: 'Details',
                            fields: [
                                { field: 'orderId', dataType: 'number' },
                                { field: 'productId', dataType: 'number' },
                                { field: 'unitPrice', dataType: 'number' },
                                { field: 'quantity', dataType: 'number' },
                                { field: 'discount', dataType: 'number' }
                            ]
                        }
                    ]
                }
            ]
        }
    ];
    

    초기 필터 설정

    Now let's add initial filtering rules to our grid so that the root grid is filtered when first loaded. We will create a FilteringExpressionsTree and set it to the advancedFilteringExpressionsTree property of the IgxHierarchicalGrid using the ngOnInit lifecycle hook.

    Let's say we want to filter customers that have order's freight at least 500. We will take advantage of the ability to create nested queries in the filtering expressions and this is the result:

    public ngOnInit() {
        const ordersTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Orders', ['customerId']);
        ordersTree.filteringOperands.push({
            fieldName: 'freight',
            ignoreCase: false,
            condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo'),
            conditionName: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo').name,
            searchVal: '500'
        });
    
        const customersTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Customers', ['customerId', 'companyName', 'contactName', 'contactTitle']);
        customersTree.filteringOperands.push({
            fieldName: 'customerId',
            condition: IgxStringFilteringOperand.instance().condition('inQuery'),
            conditionName: IgxStringFilteringOperand.instance().condition('inQuery').name,
            ignoreCase: false,
            searchTree: ordersTree
        });
        this.hGrid.advancedFilteringExpressionsTree = customersTree;
    }
    

    Connecting to the endpoint

    We will be communicating with the endpoint over HTTP protocol using the XMLHttpRequest interface the browsers provide. In order to achieve this more easily we will use Angular's HttpClient module that offers a simplified client HTTP API.

    루트 그리드 데이터 가져오기

    The Northwind WebAPI provides us with a POST endpoint that accepts an IFilteringExpressionsTree as a parameter and we will use it in order to take advantage of the Advanced Filtering functionality in the IgxHierarchicalGrid and filter records in the root grid. We will do this in refreshRootGridData method:

    public refreshRootGridData() {
        const tree = this.hGrid.advancedFilteringExpressionsTree;
        this.hGrid.isLoading = true;
        if (tree) {
            this.http.post(`${API_ENDPOINT}/QueryBuilder/ExecuteQuery`, tree).subscribe(data =>{
                this.remoteData = Object.values(data)[0];
                this.hGrid.isLoading = false;
                this.hGrid.cdr.detectChanges();
            });
        } else {
            this.http.get(`${API_ENDPOINT}/Customers`).subscribe(data => {
                this.remoteData = Object.values(data);
                this.hGrid.isLoading = false;
                this.hGrid.cdr.detectChanges();
            });
        }
    }
    

    As you can see this.http will be a reference to our HttpCLient module. The subscribe method is part of Angular's Observable and is used to handle the asynchronous response from the HTTP request. When the data is received, it assigns the fetched data to the relevant grid, updates its loading state to false, and triggers change detection to ensure the UI reflects the changes.

    In order to load the data after the root grid is initially rendered, we will use the ngAfterViewInit lifecycle hook and call the refreshRootGridData method:

    public ngAfterViewInit() {
        this.refreshRootGridData();
    }
    

    자식 그리드 데이터 가져오기

    다음으로 하위 그리드에 대한 데이터를 가져오기 위해 GET 요청에 대한 URL을 빌드하는 방법을 정의합니다. 다음은 테이블 간의 관계를 시각적으로 표현한 것입니다.

    Relational Hierarchical Database

    Finally, we need to implement our gridCreated method that will request data for any new child grid created. It will be similar to getting the root level grid data, just this time we will use the data provided in the event gridCreated and build our URL with it:

    public gridCreated(event: IGridCreatedEventArgs) {
        event.grid.isLoading = true;
        const url = this.buildUrl(event);
        this.http.get(url).subscribe(data => {
            event.grid.data = Object.values(data);
            event.grid.isLoading = false;
            this.hGrid.cdr.detectChanges();
        });
    }
    
    private buildUrl(event: IGridCreatedEventArgs) {
        const parentKey = (event.grid.parent as any).key ?? this.schema[0].name;
        const url = `${API_ENDPOINT}/${parentKey}/${event.parentID}/${event.owner.key}`;
        return url;
    }
    

    API References

    Additional Resources

    우리 커뮤니티는 활동적이며 항상 새로운 아이디어를 환영합니다.