Angular Grid の編集と検証

    Grid の編集は、セル/行の編集時のユーザー入力の組み込み検証メカニズムを公開します。これは Angular Form 検証機能を拡張し、既知の機能と簡単に統合できるようにします。エディターの状態が変更されると、視覚的なインジケーターが編集されたセルに適用されます。

    構成

    テンプレート駆動で構成する

    Angular Forms 検証ディレクティブは、IgxColumn で直接動作するよう拡張されています。同じ検証が igx-column で宣言的に設定される属性として利用できます。以下の検証は追加設定なしでサポートされます。

    • required
    • min
    • max
    • email
    • minlength
    • maxlength
    • pattern

    列入力が設定され、値がメールとして書式設定されることを検証するには、関連するディレクティブを使用できます。

    <igx-column [field]="email" [header]="User E-mail" required email></igx-column>
    

    以下のサンプルは、Grid に組み込み済みの requiredemail および min 検証ディレクティブを使用する方法を示しています。

    リアクティブ フォームで構成する

    formGroupCreated イベントを介して行/セルで編集を開始するときに検証に使用する FormGroup を公開します。関連するフィールドに独自の検証を追加して変更できます。

    <igx-grid (formGroupCreated)='formCreateHandler($event)' ...>
    
        public formCreateHandler(args: IGridFormGroupCreatedEventArgs) {
            const formGroup = args.formGroup;
            const orderDateRecord = formGroup.get('OrderDate');
            const requiredDateRecord = formGroup.get('RequiredDate');
            const shippedDateRecord = formGroup.get('ShippedDate');
    
            orderDateRecord.addValidators(this.futureDateValidator());
            requiredDateRecord.addValidators(this.pastDateValidator());
            shippedDateRecord.addValidators(this.pastDateValidator());
        }
    

    独自の検証関数を作成するか、組み込みの Angular 検証関数を使用できます。

    検証サービス API

    グリッドは、validation プロパティを介して検証サービスを公開します。 このサービスには以下のパブリック API があります。

    • valid - グリッドの検証状態が有効であるかどうかを返します。
    • getInvalid - 無効な状態のレコードを返します。
    • clear - レコードの状態を ID でクリアします。ID が提供されない場合はすべてのレコードの状態をクリアします。
    • markAsTouched - 関連するレコード/フィールドをタッチ済みとしてマークします。

    無効な状態は、検証ルールに従って検証エラーが修正されるか、クリアされるまで保持されます。

    検証トリガー

    検証は以下のシナリオでトリガーされます。

    • グリッドの validationTrigger に基づくセルエディターでの編集中。エディター入力中の変更時 (change)、またはエディターがフォーカスを失うか (blur) 閉じた場合。
    • updateRowupdateCell などの API を使用してセル/行を更新する場合 。
    • トランザクション サービスの一括編集および undo/redo API を使用する場合。

    注: ユーザー入力または編集 API で編集されていないレコードに対しては、検証はトリガーされません。セルの視覚的なインジケーターは、ユーザー操作または検証サービスの markAsTouched API を介して入力がタッチ済みと見なされる場合のみ表示されます。

    Angular Grid 検証のカスタマイズ オプション

    カスタム検証を設定する

    テンプレート内の <igx-column> で使用する独自の検証ディレクティブを定義することができます。

    @Directive({
        selector: '[phoneFormat]',
        providers: [{ provide: NG_VALIDATORS, useExisting: PhoneFormatDirective, multi: true }]
    })
    export class PhoneFormatDirective extends Validators {
        @Input('phoneFormat')
        public phoneFormatString = '';
    
        public validate(control: AbstractControl): ValidationErrors | null {
            return this.phoneFormatString ? phoneFormatValidator(new RegExp(this.phoneFormatString, 'i'))(control)
                : null;
        }
    }
    

    定義して app モジュールに追加した以降、宣言的にグリッドの指定の列に設定できます。

    <igx-column phoneFormat="\+\d{1}\-(?!0)(\d{3})\-(\d{3})\-(\d{4})\b" ...>
    

    デフォルトのエラー テンプレートを変更する

    セルが無効な状態になったときにエラー ツールチップに表示されるカスタム エラー テンプレートを定義できます。 これは、カスタム エラー メッセージを追加したり、メッセージの外観やコンテンツを変更したりする場合に便利です。

    <igx-column ... >
      <ng-template igxCellValidationError let-cell='cell' let-defaultErr="defaultErrorTemplate">
          <ng-container *ngTemplateOutlet="defaultErr">
          </ng-container>
          <div *ngIf="cell.validation.errors?.['phoneFormat']">
            Please enter correct phone format
          </div>
      </ng-template>
    </igx-column>
    

    無効な状態での編集モードの終了を防止する

    場合によっては、データ中の無効な値を送信しないようにしたいことがあります。 その場合は、cellEdit または rowEdit イベントを使用し、新しい値が無効な場合にイベントをキャンセルできます。 いずれのイベントも引数には valid プロパティがあり、これによってキャンセルできます。

    <igx-grid (cellEdit)='cellEdit($event)' ...>
    
    public cellEdit(evt) {
      if (!evt.valid) {
        evt.cancel = true;
      }
    }
    

    以下の例は、上記のカスタマイズ オプションを示しています。

    クロス フィールド検証

    場合によっては、1 つのフィールドの検証がレコード内の別のフィールドの値に依存することがあります。 その場合、カスタム検証を使用して共有 FormGroup を介してレコード内の値を比較できます。

    以下のサンプルは、同じレコードの異なるフィールド間のクロスフィールド検証を示しています。レコードのアクティブな日付と作成日付とを現在の日付と比較した有効性、および各従業員の商談成立/失効を確認します。すべてのエラーは別のピン固定列に収集され、レコードが無効であることを示し、関連するエラーを表示します。

    次のコード行は、比較を含み、それらに関連する関連エラーを設定するクロス フィールド検証関数を示しています。

    private rowValidator(): ValidatorFn {
        return (formGroup: FormGroup): ValidationErrors | null => {
            let returnObject = {};
            const createdOnRecord = formGroup.get('created_on');
            const lastActiveRecord = formGroup.get('last_activity');
            const winControl = formGroup.get('deals_won');
            const loseControl = formGroup.get('deals_lost');
            const actualSalesControl = formGroup.get('actual_sales');
    
            // Validate dates
            const curDate = new Date();
            if (new Date(createdOnRecord.value) > curDate) {
                // The created on date shouldn't be greater than current date.
                returnObject['createdInvalid'] =  true;
            }
            if (new Date(lastActiveRecord.value) > curDate) {
                // The last active date shouldn't be greater than current date.
                returnObject['lastActiveInvalid'] = true;
            }
            if (new Date(createdOnRecord.value) > new Date(lastActiveRecord.value)) {
                // The created on date shouldn't be greater than last active date.
                returnObject['createdLastActiveInvalid'] = true;
            }
    
            // Validate deals
            const dealsRatio = this.calculateDealsRatio(winControl.value, loseControl.value);
            if (actualSalesControl.value === 0 && dealsRatio > 0) {
                // If the actual sales value is 0 but there are deals made.
                returnObject['salesZero'] = true;
            }
            if (actualSalesControl.value > 0 && dealsRatio === 0) {
                // If the deals ratio based on deals won is 0 but the actual sales is bigger than 0.
                returnObject['salesNotZero'] = true;
            }
    
            return returnObject;
        };
    }
    
    public calculateDealsRatio(dealsWon, dealsLost) {
        if (dealsLost === 0) return dealsWon + 1;
        return Math.round(dealsWon / dealsLost * 100) / 100;
    }
    

    クロス フィールド検証は、編集モードに入ったときに各行の新しい formGroup を返す formGroupCreated イベントから、その行の formGroup に追加することができます。

    <igx-grid #grid1 [data]="transactionData" [width]="'100%'" [height]="'480px'" [autoGenerate]="false" 
            [batchEditing]="true" [rowEditable]="true" [primaryKey]="'id'"
            (formGroupCreated)='formCreateHandler($event)'>
        <!-- ... -->
    </igx-grid>
    
    public formCreateHandler(evt: IGridFormGroupCreatedEventArgs) {
        evt.formGroup.addValidators(this.rowValidator());
    }
    

    異なるエラーはテンプレート セルに表示され、すべてのエラーは一つのツールチップに結合されます。行の有効状態に応じて、異なるアイコンが表示されます。

    <igx-column field="row_valid" header=" " [editable]="false" [pinned]="true" [width]="'50px'">
        <ng-template igxCell let-cell="cell">
            <div *ngIf="isRowValid(cell)" [igxTooltipTarget]="tooltipRef"  style="margin-right: '-10px';">
                <img width="18" src="assets/images/grid/active.png"/>
            </div>
            <div *ngIf="!isRowValid(cell)" [igxTooltipTarget]="tooltipRef" style="margin-right: '-10px';">
                <img width="18" src="assets/images/grid/expired.png"/>
            </div>
            <div #tooltipRef="tooltip" igxTooltip [style.width]="'max-content'">
                <div *ngFor="let message of stateMessage(cell)">
                    {{message}}
                </div>
            </div>
        </ng-template>
    </igx-column>
    

    各列にはテンプレート化されたフォーム検証があり、カスタム rowValidator によって行ごとのエラーを確認するため、エラー メッセージ は各セルのエラーを収集する stateMessage 関数で収集されます。

    public stateMessage(cell: IgxGridCell) {
        const messages = [];
        const row = cell.row;
        const cellValidationErrors = row.cells.filter(x => !!x.validation.errors);
        cellValidationErrors.forEach(cell => {
            if (cell.validation.errors) {
                if (cell.validation.errors.required) {
                    messages.push(`The \`${cell.column.header}\` column is required.`);
                }
                // Other cell errors ...
            }
        });
    
        if (row.validation.errors?.createdInvalid) {
            messages.push(`The \`Date of Registration\` date cannot be in the future.`);
        }
        // Other cross-field errors...
    
        return messages;
    }
    

    以下のサンプルは、クロス フィールド検証の動作を示しています。

    スタイル設定

    Ignite UI for Angular テーマ ライブラリを使用して、編集時のデフォルトの検証スタイルを変更できます。

    以下の例では、検証メッセージの公開されたテンプレートを使用します。ツールチップをポップアウトし、および、検証のデフォルトの外観を変更するためにエラー時の色をオーバーライドします。 また、無効な行をより明確にするために背景のスタイルを設定します。

    テーマのインポート

    スタイルを設定し、css 変数にアクセスする最も簡単な方法は、app のグローバル スタイル ファイル (通常 は styles.scss です) でスタイルを定義することです。 はじめに themes/index ファイルをインポートすることにより、Ignite UI for Angular Sass フレームワークの強力なツールへアクセスできるようになります。

    @use "igniteui-angular/theming" as *;
    
    // IMPORTANT: Prior to Ignite UI for Angular version 13 use:
    // @import '~igniteui-angular/lib/core/styles/themes/index';
    

    スタイルを含める

    エラーの色を変更するには、css 変数 --igx-error-500 を使用します。

    --igx-error-500: 34, 80%, 63%;
    

    カスタム テンプレート

    デフォルトのエラー テンプレートを変更することで、カスタム クラスとスタイルを設定できます。

    <ng-template igxCellValidationError let-cell='cell' let-defaultErr='defaultErrorTemplate'>
        <div class="validator-container">
            <ng-container *ngTemplateOutlet="defaultErr">
            </ng-container>
        </div>
    </ng-template>
    

    無効な行とセルのスタイル

    行とセルは、開発者が行またはセルが無効かどうか、およびアクティブなエラーの種類を知るための API を提供します。

    public rowStyles = {
        background: (row: RowType) => row.validation.status === 'INVALID' ? '#FF000033' : '#00000000'
    };
    public cellStyles = {
        'invalid-cell': (rowData, columnKey) => {
            const pKey = this.grid.primaryKey;
            const cell = this.grid.getCellByKey(rowData[pKey], columnKey);
            return cell && cell.validation.status === 'INVALID';
        }
    }
    
    <igx-grid [rowStyles]="rowStyles">
        <igx-column field="ReorderLevel" header="ReorderLever" required [cellClasses]="cellStyles">
    

    デモ

    API リファレンス

    その他のリソース

    コミュニティに参加して新しいアイデアをご提案ください。