IgxGrid は ISortingStrategy を実装することでソート方法をカスタマイズすることができます。

この方法を応用して、欠損値として “-” や “*” や “” (空文字列) などが使われている場合に、欠損値を null や undefined と同じとみなしてソートする方法をご紹介します。

この記事で扱うデータ

下のコードはこの記事で使用するデータです。Col1 フィールドは数値型もしくは文字列型と定義されていて、欠損値として “-” が使用されています。

// src\app\sampleData.ts

export interface SampleDataType {
    ID: number;
    Col1: number | string;
}

export const SampleData: SampleDataType[] = [
    { ID: 1, Col1: 1 },
    { ID: 2, Col1: 8 },
    { ID: 3, Col1: 5 },
    { ID: 4, Col1: '-' },   // 欠損値
    { ID: 5, Col1: 3 },
    { ID: 6, Col1: '-' },   // 欠損値
    { ID: 7, Col1: 6 },
    { ID: 7, Col1: 12 },
]

このデータを IgxGrid にバインドして Col1 で並べ替えた場合に、昇順のときは ‘-‘ が一番最初に、降順のときは一番最後にさせてみましょう。

 

第一段階 : データを igx-grid に表示するまで

App コンポーネント側

// src\app\app.component.ts

import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { IgxGridModule, ISortingStrategy} from '@infragistics/igniteui-angular';
import { SampleDataType, SampleData } from './sampleData';    // 「この記事で扱うデータ」で提示しているデータを読み込みます

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, IgxGridModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent implements OnInit {
  title = 'kb15005-app1';
  public data: SampleDataType[] = SampleData;    // 読み込んだデータを data に設定します。

  constructor() {}

  ngOnInit() {
  }
}
  • 7 行目 : この一つ前のセクション「この記事で扱うデータ」で提示したデータを読み込みます。
  • 17 行目 : 読み込んだデータを data に設定します。

App コンポーネント テンプレート側

<!-- src\app\app.component.html -->

<div class="my_wrapper">
  <igx-grid #grid1 [data]="data" [autoGenerate]="false" [primaryKey]="'ID'" height="600px">
    <igx-column field="ID" header="ID" [dataType]="'number'" [sortable]="true"></igx-column>
    <igx-column field="Col1" header="Col1" [dataType]="'string'" [sortable]="true">
    </igx-column>
  </igx-grid>
</div>
  • 4 行目 : igx-grid の data に読み込んだデータを設定します。
  • 6 行目 : Col1 の dataType を string にしていることに注目です。SampleDataType の Col1 フィールドの定義を思い出してください。数値もしくは文字列でした。ここで dataType を number としてしまうと、数値に変換できない文字列が来たときにエラーが発生して表示できません。string であればどのようなものが来ても igx-grid は string に変換して表示させようとしますので、そのようなエラーを回避することができます。
  • 5、6 行目の各 igx-column : sortable を trueにして並べ替え機能を有効にします。

実行結果

igx-grid にデータを表示させ、ソート機能を有効にするところまでできました。

 

第二段階 : ISortingStrategy を実装したクラスを実装する

今回は CustomSortingStrategy という名前のクラスを作成し、そこに ISortingStrategy を実装していきましょう。

まず src フォルダーの下に lib フォルダーを作成し、その下に CustomSortingStrategy.ts ファイルを作成します。

 

ISortingStrategy を実装したクラスをゼロから作成するのは大変ですので、igniteui-angular に組み込まれているデフォルトの ISortingStrategy の実装クラスをコピーしてきて必要な箇所だけ変更していくことにします。デフォルトの ISortingStrategy の実装クラスは GitHub にあり、こちらの DefaultSortingStrategy クラスがそれです。

コピーしてきたら、compareValue の null 値かもしくは undefined 値かを判定している部分に、’-‘ かどうかも追加します。

// src\lib\CustomSortingStrategy.ts

public compareValues(a: any, b: any): number {
    const an = (a === null || a === undefined || a === '-');    // '-'もnullやundefined判定のところに加える
    const bn = (b === null || b === undefined || b === '-');    // '-'もnullやundefined判定のところに加える
    if (an) {
        if (bn) {
            return 0;
        }
        return -1;
    } else if (bn) {
        return 1;
    }
    return a > b ? 1 : a < b ? -1 : 0;
}

また、本題とは離れますが、このままでは一部のコードでエラーが発生してしまうので、そこも修正しておきます。

// src\lib\CustomSortingStrategy.ts

... (中略) ...

// コンストラクターをpublicに変更します。
public constructor() { }

... (中略) ...

public sort( ... 中略 ... )
) {
    const key = fieldName;
    const reverse = (dir === SortingDirection.Desc ? -1 : 1);
    // isDateおよびisTimeがnullのときはfalseを設定するように変更します。
    const cmpFunc = (obj1: any, obj2: any) => this.compareObjects(obj1, obj2, key, reverse, ignoreCase, valueResolver, isDate ?? false, isTime ?? false);
    return this.arraySort(data, cmpFunc);
}

これで ISortingStrategy を実装した CustomSortingStrategy クラスの実装は完成です。最終的にはこのようなコードになります。

// src\lib\CustomSortingStrategy.ts

import { ISortingStrategy, SortingDirection } from '@infragistics/igniteui-angular';

export class CustomSortingStrategy implements ISortingStrategy {
    protected static _instance: CustomSortingStrategy | null = null;

    // コンストラクターをpublicに変更します。
    public constructor() { }

    public static instance(): CustomSortingStrategy {
        return this._instance || (this._instance = new this());
    }

    public sort(
        data: any[],
        fieldName: string,
        dir: SortingDirection,
        ignoreCase: boolean,
        valueResolver: (obj: any, key: string, isDate?: boolean) => any,
        isDate?: boolean,
        isTime?: boolean
    ) {
        const key = fieldName;
        const reverse = (dir === SortingDirection.Desc ? -1 : 1);
        // isDateおよびisTimeがnullのときはfalseを設定するように変更します。
        const cmpFunc = (obj1: any, obj2: any) => this.compareObjects(obj1, obj2, key, reverse, ignoreCase, valueResolver, isDate ?? false, isTime ?? false);
        return this.arraySort(data, cmpFunc);
    }

    public compareValues(a: any, b: any): number {
        const an = (a === null || a === undefined || a === '-');    // '-'をnullやundefinedと同じとして扱う処理を追加します
        const bn = (b === null || b === undefined || b === '-');    // '-'をnullやundefinedと同じとして扱う処理を追加します
        if (an) {
            if (bn) {
                return 0;
            }
            return -1;
        } else if (bn) {
            return 1;
        }
        return a > b ? 1 : a < b ? -1 : 0;
    }

    protected compareObjects(
        obj1: any,
        obj2: any,
        key: string,
        reverse: number,
        ignoreCase: boolean,
        valueResolver: (obj: any, key: string, isDate?: boolean, isTime?: boolean) => any,
        isDate: boolean,
        isTime: boolean
    ) {
        let a = valueResolver.call(this, obj1, key, isDate, isTime);
        let b = valueResolver.call(this, obj2, key, isDate, isTime);
        if (ignoreCase) {
            a = a && a.toLowerCase ? a.toLowerCase() : a;
            b = b && b.toLowerCase ? b.toLowerCase() : b;
        }
        return reverse * this.compareValues(a, b);
    }

    protected arraySort(data: any[], compareFn?: (arg0: any, arg1: any) => number): any[] {
        return data.sort(compareFn);
    }
}

 

第三段階 : CustomSortingStrategy を Col1 列に適用する

ここまでくればあともう一息です!

App コンポーネント側

作成した CustomSortingStrategy をインポートし、オブジェクトを作成します。

// src\app\app.component.ts

import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { IgxGridModule, ISortingStrategy} from '@infragistics/igniteui-angular';
import { SampleDataType, SampleData } from './sampleData';
import { CustomSortingStrategy } from '../lib/CustomSortingStrategy'; // ここ!CustomSortingStrategyをインポートします。

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, IgxGridModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent implements OnInit {
  title = 'kb15005-app1';
  public data: SampleDataType[] = SampleData;
  public col1CustomSortingStrategy: ISortingStrategy = new CustomSortingStrategy(); // ここ!CustomSortingStrategyオブジェクトを作成し、col1CustomSortingStrategyに代入します。

  constructor() {}

  ngOnInit() {
  }
}

App コンポーネント テンプレート側

Col1 の igx-column の sortStrategy で CustomSortingStrategy オブジェクトを指定します。

<!-- src\app\app.component.html -->

<div class="my_wrapper">
  <igx-grid #grid1 [data]="data" [autoGenerate]="false" [primaryKey]="'ID'" height="600px">
    <igx-column field="ID" header="ID" [dataType]="'number'" [sortable]="true"></igx-column>

    <!--
    sortStrategyでCol1のカスタムソートを設定します。
    -->
    <igx-column
      field="Col1" header="Col1" [dataType]="'string'" [sortable]="true"
      [sortStrategy]="col1CustomSortingStrategy">
    </igx-column>

  </igx-grid>
</div>

以上ですべて完了です!

実行結果

Col1 列を昇順で並べ替えた場合

 

“-” が先頭に来るようになりました! しかも 12 も数値として並べ替えられていて、ちゃんと最後に来るようになっています。

Col1 列を降順で並べ替えた場合

 

こちらも同様ですね! “-” が最後に来て、12 も数値として並べ替えられています。

 

 

Tagged:

製品について

Ignite UI for Angular