Angular 트리 그리드 검색 필터

    Angular Tree Grid 검색은 데이터 컬렉션에서 값을 찾는 프로세스를 가능하게 합니다. 이 기능을 설정하는 것을 더 쉽게 만들고 검색 입력 상자, 버튼, 키보드 탐색 및 기타 유용한 기능으로 구현하여 더 나은 사용자 경험을 제공할 수 있습니다. 브라우저가 기본적으로 콘텐츠 검색 기능을 제공하지만 대부분의 경우 Tree Grid는 보이지 않는 열과 행을 가상화합니다. 이러한 경우 기본 그리드 검색은 DOM의 일부가 아니기 때문에 가상화된 셀에서 데이터를 검색할 수 없습니다. Ignite UI for Angular 테이블 기반 그리드를 확장하여 Tree Grid의 가상화된 콘텐츠를 검색할 수 있는 검색 API를 제공했습니다.

    Angular Search Example

    다음 예는 모든 열과 행을 검색할 수 있는 검색 입력 상자와 각 열에 대한 특정 필터링 옵션이 있는 트리 그리드를 나타냅니다.

    Angular Search Usage

    Grid setup

    그리드를 생성하고 이를 데이터에 바인딩하는 것부터 시작해 보겠습니다. 또한 우리가 사용할 구성 요소에 대한 몇 가지 사용자 정의 스타일을 추가할 것입니다!

    <!--searchgrid.component.html-->
    
    <igx-tree-grid #treeGrid1 [data]="data" [autoGenerate]="false" primaryKey="ID" foreignKey="ParentID" [allowFiltering]="true">
        <igx-column [field]="'Name'" dataType="string" [sortable]="true"></igx-column>
        <igx-column [field]="'ID'" dataType="number" [sortable]="true"></igx-column>
        <igx-column [field]="'Title'" dataType="string" [sortable]="true"></igx-column>
        <igx-column [field]="'Age'" dataType="number" [sortable]="true"></igx-column>
        <igx-column [field]="'HireDate'" dataType="date" [sortable]="true"></igx-column>
    </igx-tree-grid>
    
    /* searchgrid.component.css */
    
    .grid__wrapper {
        margin: 15px;
    }
    
    .offset {
        margin-bottom: 15px;
    }
    
    .resultsText {
        font-size: 0.875rem;
    }
    
    .chips {
        margin-left: 5px;
    }
    
    .searchButtons {
        margin-left: 5px;
    }
    

    좋습니다. 이제 트리 그리드의 검색 API를 준비하겠습니다! 현재 검색된 텍스트를 저장하고 검색이 대소문자를 구분하는지 및/또는 정확히 일치하는지 여부를 저장하는 데 사용할 수 있는 몇 가지 속성을 만들 수 있습니다.

    // searchgrid.component.ts
    
    public searchText: string = '';
    public caseSensitive: boolean = false;
    public exactMatch: boolean = false;
    

    Angular search box input

    Now let's create our search input! By binding our searchText as ngModel to our newly created input and subscribe to the ngModelChange event, we can detect every single searchText modification by the user. This will allow us to use the Tree Grid's findNext and findPrev methods to highlight all the occurrences of the searchText and scroll to the next/previous one (depending on which method we have invoked).

    Both the findNext and the findPrev methods have three arguments:

    • text: string (the text we are searching for)
    • (optional) caseSensitive: boolean (should the search be case sensitive or not, default value is false)
    • (optional) exactMatch: boolean (should the search be by an exact match or not, default value is false)

    정확한 일치로 검색할 때 검색 API는 대소문자 구분도 고려하여 searchText와 완전히 일치하는 셀 값만 결과로 강조 표시합니다. 예를 들어 'software' 및 'Software' 문자열은 대소문자 구분을 무시하고 정확히 일치합니다.

    위의 메소드는 숫자 값(트리 그리드에 주어진 문자열이 포함된 횟수)을 반환합니다.

    <!--searchgrid.component.html-->
    
    <input #search1 id="search1" placeholder="Search" [(ngModel)]="searchText" (ngModelChange)="treeGrid.findNext(searchText, caseSensitive, exactMatch)" />
    

    Display results count

    Let's also display the position of the current occurrence, along with the total results count! We can do this by using the grid's lastSearchInfo property. This property is automatically updated when using the find methods.

    • The treeGrid.lastSearchInfo.matchInfoCache.length value will give us the total results count.
    • The treeGrid.lastSearchInfo.activeMatchIndex value will give us the index position of the current occurrence (match).
    <!--searchgrid.component.html-->
    
    <div class="resultsText" *ngIf="treeGrid.lastSearchInfo">
        <span *ngIf="treeGrid.lastSearchInfo.matchInfoCache.length > 0">
            {{ treeGrid.lastSearchInfo.activeMatchIndex + 1 }} of {{ treeGrid.lastSearchInfo.matchInfoCache.length }} results
        </span>
        <span *ngIf="treeGrid.lastSearchInfo.matchInfoCache.length == 0">
            No results
        </span>
    </div>
    

    Add search buttons

    In order to freely search and navigate among our search results, let's create a couple of buttons by invoking the findNext and the findPrev methods inside the buttons' respective click event handlers.

    <!--searchgrid.component.html-->
    
    <div class="searchButtons">
        <input type="button" value="Previous" (click)="treeGrid.findPrev(searchText, caseSensitive, exactMatch)" />
        <input type="button" value="Next" (click)="treeGrid.findNext(searchText, caseSensitive, exactMatch)" />
    </div>
    

    We can also allow the users to navigate the results by using the keyboard's arrow keys and the Enter key. In order to achieve this, we can handle the keydown event of our search input by preventing the default caret movement of the input with the preventDefault() method and invoke the findNext/findPrev methods depending on which key the user has pressed.

    <!--searchgrid.component.html-->
    
    <input #search1 id="search1" placeholder="Search" [(ngModel)]="searchText" (ngModelChange)="treeGrid.findNext(searchText, caseSensitive, exactMatch)"
           (keydown)="searchKeyDown($event)" />
    
    // searchgrid.component.ts
    
    public searchKeyDown(ev) {
        if (ev.key === 'Enter' || ev.key === 'ArrowDown' || ev.key === 'ArrowRight') {
            ev.preventDefault();
            this.treeGrid.findNext(this.searchText, this.caseSensitive, this.exactMatch);
        } else if (ev.key === 'ArrowUp' || ev.key === 'ArrowLeft') {
            ev.preventDefault();
            this.treeGrid.findPrev(this.searchText, this.caseSensitive, this.exactMatch);
        }
    }
    

    Case sensitive and Exact match

    Now let's allow the user to choose whether the search should be case sensitive and/or by an exact match. For this purpose we can use simple checkbox inputs by binding our caseSensitive and exactMatch properties to the inputs' checked properties respectively and handle their change events by toggling our properties and invoking the findNext method.

    <!--searchgrid.component.html-->
    
    <span>Case sensitive</span>
    <input type="checkbox" [checked]="caseSensitive" (change)="updateSearch()">
    
    <span>Exact match</span>
    <input type="checkbox" [checked]="exactMatch" (change)="updateExactSearch()">
    
    // searchgrid.component.ts
    
    public updateSearch() {
        this.caseSensitive = !this.caseSensitive;
        this.treeGrid.findNext(this.searchText, this.caseSensitive, this.exactMatch);
    }
    
    public updateExactSearch() {
        this.exactMatch = !this.exactMatch;
        this.treeGrid.findNext(this.searchText, this.caseSensitive, this.exactMatch);
    }
    

    Persistence

    What if we would like to filter and sort our Tree Grid or even to add and remove records? After such operations, the highlights of our current search automatically update and persist over any text that matches the searchText! Furthermore, the search will work with paging and will persist the highlights through changes of the Tree Grid's perPage property.

    Adding icons

    다른 구성 요소 중 일부를 사용하면 풍부한 사용자 인터페이스를 만들고 전체 검색 창의 전반적인 디자인을 개선할 수 있습니다! 검색 입력 왼쪽에는 멋진 검색 또는 삭제 아이콘이 있고, 검색 옵션을 위한 몇 가지 칩이 있으며, 오른쪽에는 탐색을 위한 멋진 잔물결 스타일 버튼과 결합된 일부 머티리얼 디자인 아이콘이 있습니다. 보다 세련된 디자인을 위해 이러한 구성 요소를 입력 그룹 내에 래핑할 수 있습니다. 이를 위해 IgxInputGroup, IgxIcon, IgxRipple, IgxButtonIgxChip 모듈을 가져와 보겠습니다.

    // app.module.ts
    
    ...
    import {
        IgxTreeGridModule,
        IgxInputGroupModule,
        IgxIconModule,
        IgxRippleModule,
        IgxButtonModule,
        IgxChipsModule
    } from 'igniteui-angular';
    // import { 
    //    IgxInputGroupModule,
    //    IgxIconModule,
    //    IgxRippleModule,
    //    IgxButtonModule,
    //    IgxChipsModule
    // } from '@infragistics/igniteui-angular'; for licensed package
    
    @NgModule({
        ...
        imports: [..., IgxInputGroupModule, IgxIconModule, IgxRippleModule, IgxButtonModule, IgxChipsModule],
    })
    export class AppModule {}
    

    마지막으로 템플릿을 새로운 구성요소로 업데이트해 보겠습니다.

    We will wrap all of our components inside an IgxInputGroup. On the left we will toggle between a search and a delete/clear icon (depending on whether the search input is empty or not). In the center, we will position the input itself. In addition, whenever the delete icon is clicked, we will update our searchText and invoke the Tree Grid's clearSearch method to clear the highlights.

    <!--searchgrid.component.html-->
    
    <igx-input-group type="search" class="offset">
        <igx-prefix>
            <igx-icon *ngIf="searchText.length == 0">search</igx-icon>
            <igx-icon *ngIf="searchText.length > 0" (click)="clearSearch()">clear</igx-icon>
        </igx-prefix>
    
        <input #search1 id="search1" igxInput placeholder="Search" [(ngModel)]="searchText" (ngModelChange)="treeGrid.findNext(searchText, caseSensitive, exactMatch)"
            (keydown)="searchKeyDown($event)" />
    
        <igx-suffix *ngIf="searchText.length > 0">
            ...
        </igx-suffix>
    </igx-input-group>
    
    // searchgrid.component.ts
    
    public clearSearch() {
        this.searchText = '';
        this.treeGrid.clearSearch();
    }
    

    입력 그룹 오른쪽에 다음 목적을 가진 세 개의 별도 컨테이너를 만들어 보겠습니다.

    • 검색 결과를 표시합니다.
    <!--searchgrid.component.html-->
    
    <igx-suffix *ngIf="searchText.length > 0">
        <div class="resultsText" *ngIf="treeGrid.lastSearchInfo">
            <span *ngIf="treeGrid.lastSearchInfo.matchInfoCache.length > 0">
                {{ treeGrid.lastSearchInfo.activeMatchIndex + 1 }} of {{ treeGrid.lastSearchInfo.matchInfoCache.length }} results
            </span>
            <span *ngIf="treeGrid.lastSearchInfo.matchInfoCache.length == 0">
                No results
            </span>
        </div>
    </igx-suffix>
    
    • CaseSensitiveExactMatch 속성을 전환하는 몇 가지 칩을 표시합니다. 우리는 체크박스를 이러한 속성에 따라 색상을 변경하는 두 개의 세련된 칩으로 대체했습니다. 칩을 클릭할 때마다 클릭한 칩에 따라 해당 핸들러(updateSearch 또는 updateExactSearch)를 호출합니다.
    <!--searchgrid.component.html-->
    
        ...
        <div class="chips">
            <igx-chips-area>
                <igx-chip (click)="updateSearch()" [color]="caseSensitive? 'lightgrey' : 'rgba(0, 0, 0, .04)'">
                    <span>Case Sensitive</span>
                </igx-chip>
                <igx-chip (click)="updateExactSearch()" [color]="exactMatch? 'lightgrey' : 'rgba(0, 0, 0, .04)'">
                    <span>Exact Match</span>
                </igx-chip>
            </igx-chips-area>
        </div>
        ...
    
    • For the search navigation buttons, we have transformed our inputs into ripple styled buttons with material icons. The handlers for the click events remain the same - invoking the findNext/findPrev methods.
    <!--searchgrid.component.html-->
    
    <igx-suffix>
        <div class="searchButtons">
            <button igxIconButton="flat" igxRipple igxRippleCentered="true" (click)="treeGrid.findPrev(searchText, caseSensitive, exactMatch)">
                <igx-icon fontSet="material">navigate_before</igx-icon>
            </button>
            <button igxIconButton="flat" igxRipple igxRippleCentered="true" (click)="treeGrid.findNext(searchText, caseSensitive, exactMatch)">
                <igx-icon fontSet="material">navigate_next</igx-icon>
            </button>
        </div>
    </igx-suffix>
    

    Known Limitations

    한정 설명
    템플릿을 사용하여 셀에서 검색 검색 기능 강조 표시는 기본 셀 템플릿에서만 작동합니다. 사용자 정의 셀 템플릿이 포함된 열이 있는 경우 강조 표시가 작동하지 않으므로 열 포맷터와 같은 대체 접근 방식을 사용하거나searchable 열의 속성을 false로 설정합니다.
    원격 가상화 원격 가상화를 사용하면 검색이 제대로 작동하지 않습니다.
    텍스트가 잘린 셀 셀의 텍스트가 너무 커서 맞지 않고 찾고 있는 텍스트가 줄임표로 잘려도 셀로 스크롤하여 일치 횟수에 포함시키지만 아무것도 강조 표시되지 않습니다.

    API References

    이 글에서는 검색 결과 간을 탐색할 때 추가 기능이 있는 Tree Grid용 자체 검색 창을 구현했습니다. 또한 아이콘, 칩, 입력과 같은 Ignite UI for Angular도 사용했습니다. 검색 API는 아래에 나와 있습니다.

    IgxTreeGridComponent methods:

    IgxGridCell methods:

    IgxColumnComponent properties:

    ISearchInfo

    사용된 관련 API가 포함된 추가 구성 요소 및/또는 지시어:

    Styles:

    Additional Resources

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