Angular Tree Grid の編集と検証
Tree 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 >
html
以下のサンプルは、Tree Grid に組み込み済みの required
、email
および min
検証ディレクティブを使用する方法を示しています。
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
import {
IgxTreeGridModule,
IgxButtonModule,
IgxSwitchModule
} from "igniteui-angular" ;
import { TreeGridValidatorServiceComponent } from "./tree-grid/tree-grid-validator-service/tree-grid-validator-service.component" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
TreeGridValidatorServiceComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxTreeGridModule,
IgxButtonModule,
IgxSwitchModule
],
providers : [],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー
import { Component, OnInit, ViewChild } from '@angular/core' ;
import {
IgxTreeGridComponent
} from 'igniteui-angular' ;
import { generateEmployeeFlatData, IEmployee } from '../data/employees-flat' ;
@Component ({
selector : 'app-tree-grid-validator-service-component' ,
styleUrls : ['tree-grid-validator-service.component.scss' ],
templateUrl : 'tree-grid-validator-service.component.html'
})
export class TreeGridValidatorServiceComponent implements OnInit {
@ViewChild ('treeGrid' , { static : true }) public treeGrid: IgxTreeGridComponent;
public data: IEmployee[];
public rowEdit: boolean = false ;
public ngOnInit(): void {
this .data = generateEmployeeFlatData();
}
}
ts コピー <div class ="top-row" >
<igx-switch [(ngModel )]="rowEdit" > Row edit</igx-switch >
</div >
<div class ="grid__wrapper" >
<igx-tree-grid [igxPreventDocumentScroll ]="true" #treeGrid [batchEditing ]="true" [data ]="data" primaryKey ="ID"
foreignKey ="ParentID" [width ]="'100%'" [height ]="'500px'" [rowEditable ]="rowEdit" >
<igx-column field ="ID" header ="ID" dataType ="number" [editable ]="false" > </igx-column >
<igx-column field ="Age" header ="Age" dataType ="number" [editable ]="true" required min ="18" max ="99" > </igx-column >
<igx-column field ="Name" header ="Full Name" dataType ="string" [editable ]="true" required > </igx-column >
<igx-column field ="Title" header ="Title" dataType ="string" [editable ]="true" required > </igx-column >
<igx-column field ="HireDate" header ="Hire Date" dataType ="date" [editable ]="true" required > </igx-column >
<igx-column field ="OnPTO" header ="On PTO" dataType ="boolean" [editable ]="true" > </igx-column >
</igx-tree-grid >
</div >
html コピー .top-row , .grid__wrapper {
margin : 15px ;
}
scss コピー
このサンプルが気に入りましたか? 完全な Ignite UI for Angularツールキットにアクセスして、すばやく独自のアプリの作成を開始します。無料でダウンロードできます。
リアクティブ フォームで構成する
formGroupCreated
イベントを介して行/セルで編集を開始するときに検証に使用する FormGroup
を公開します。関連するフィールドに独自の検証を追加して変更できます。
<igx-tree-grid (formGroupCreated )='formCreateHandler($event)' ... >
html
public formCreateHandler (args: IGridFormGroupCreatedEventArgs ) {
const formGroup = args.formGroup;
const hireDateRecord = formGroup.get('HireDate' );
hireDateRecord.addValidators([this .futureDateValidator(), this .pastDateValidator()]);
}
ts
独自の検証関数を作成するか、組み込みの Angular 検証関数 を使用できます。
検証サービス API
グリッドは、validation
プロパティを介して検証サービスを公開します。
このサービスには以下のパブリック API があります。
valid
- グリッドの検証状態が有効であるかどうかを返します。
getInvalid
- 無効な状態のレコードを返します。
clear
- レコードの状態を ID でクリアします。ID が提供されない場合はすべてのレコードの状態をクリアします。
markAsTouched
- 関連するレコード/フィールドをタッチ済みとしてマークします。
無効な状態は、検証ルールに従って検証エラーが修正されるか、クリアされるまで保持されます。
検証トリガー
検証は以下のシナリオでトリガーされます。
注: ユーザー入力または編集 API で編集されていないレコードに対しては、検証はトリガーされません。セルの視覚的なインジケーターは、ユーザー操作または検証サービスの markAsTouched
API を介して入力がタッチ済みと見なされる場合のみ表示されます。
Angular Tree 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 ;
}
}
ts
定義して app モジュールに追加した以降、宣言的にグリッドの指定の列に設定できます。
<igx-column phoneFormat ="\+\d{1}\-(?!0)(\d{3})\-(\d{3})\-(\d{4})\b" ... >
html
デフォルトのエラー テンプレートを変更する
セルが無効な状態になったときにエラー ツールチップに表示されるカスタム エラー テンプレートを定義できます。
これは、カスタム エラー メッセージを追加したり、メッセージの外観やコンテンツを変更したりする場合に便利です。
<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 >
html
無効な状態での編集モードの終了を防止する
場合によっては、データ中の無効な値を送信しないようにしたいことがあります。
その場合は、cellEdit
または rowEdit
イベントを使用し、新しい値が無効な場合にイベントをキャンセルできます。
いずれのイベントも引数には valid
プロパティがあり、これによってキャンセルできます。その使用方法は、クロス フィールド検証の例 で確認できます。
<igx-tree-grid (cellEdit )='cellEdit($event)' ... >
html
public cellEdit (evt ) {
if (!evt.valid) {
evt.cancel = true ;
}
}
ts
例
以下の例は、上記のカスタマイズ オプションを示しています。
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
import {
IgxTreeGridModule,
IgxButtonModule
} from "igniteui-angular" ;
import { TreeGridValidatorServiceExtendedComponent } from "./tree-grid/tree-grid-validator-service-extended/tree-grid-validator-service-extended.component" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
TreeGridValidatorServiceExtendedComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxTreeGridModule,
IgxButtonModule
],
providers : [],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー
import { Component, Directive, Input, OnInit, ViewChild } from '@angular/core' ;
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms' ;
import { IgxTreeGridComponent } from 'igniteui-angular' ;
import { IGridFormGroupCreatedEventArgs } from 'igniteui-angular/lib/grids/common/grid.interface' ;
import { generateEmployeeFlatData, IEmployee } from '../data/employees-flat' ;
export function phoneFormatValidator (phoneReg: RegExp ): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const match = phoneReg.test(control.value);
return match ? null : { phoneFormat : { value : control.value } } ;
}
}
@Directive ({
selector : '[phoneFormat]' ,
providers : [{ provide : NG_VALIDATORS, useExisting : TGridPhoneFormatDirective, multi : true }]
})
export class TGridPhoneFormatDirective extends Validators {
@Input ('phoneFormat' )
public phoneFormatString = '' ;
public validate(control: AbstractControl): ValidationErrors | null {
return this .phoneFormatString ? phoneFormatValidator(new RegExp (this .phoneFormatString, 'i' ))(control)
: null ;
}
}
@Component ({
selector : 'app-tree-grid-validator-service-extended-component' ,
styleUrls : ['tree-grid-validator-service-extended.component.scss' ],
templateUrl : 'tree-grid-validator-service-extended.component.html'
})
export class TreeGridValidatorServiceExtendedComponent implements OnInit {
@ViewChild ('treeGrid' , { static : true }) public treeGrid: IgxTreeGridComponent;
public data: IEmployee[];
public ngOnInit(): void {
this .data = generateEmployeeFlatData();
}
public formCreateHandler (formGroupArgs: IGridFormGroupCreatedEventArgs ) {
const hireDateRecord = formGroupArgs.formGroup.get('HireDate' );
hireDateRecord.addValidators([this .futureDateValidator(), this .pastDateValidator()]);
}
public commit ( ) {
const invalidTransactions = this .treeGrid.validation.getInvalid();
if (invalidTransactions.length > 0 && !confirm('You\'re committing invalid transactions. Are you sure?' )) {
return ;
}
this .treeGrid.transactions.commit(this .data);
this .treeGrid.validation.clear();
}
public undo ( ) {
this .treeGrid.endEdit(true );
this .treeGrid.transactions.undo();
}
public redo ( ) {
this .treeGrid.endEdit(true );
this .treeGrid.transactions.redo();
}
public futureDateValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const date = control.value;
if (date > new Date ()){
return { futureDate : { value : control.value } };
}
return null ;
}
}
public pastDateValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const date = control.value;
let pastDate = new Date ('Sept 1 2004' );
if (pastDate){
return pastDate < date ? null : { pastDate : { value : control.value } }
} else return null ;
}
}
}
ts コピー <div class ="grid__wrapper" >
<igx-tree-grid [igxPreventDocumentScroll ]="true" #treeGrid [width ]="'100%'" [height ]="'500px'" [data ]="data" primaryKey ="ID"
foreignKey ="ParentID" [autoGenerate ]="false" [batchEditing ]="true" [rowEditable ]="true"
(formGroupCreated )="formCreateHandler($event)" >
<igx-column field ="ID" header ="ID" dataType ="number" [editable ]="false" > </igx-column >
<igx-column field ="Age" header ="Age" dataType ="number" min ="18" [editable ]="true" required max ="99" >
</igx-column >
<igx-column field ="Name" header ="Full Name" dataType ="string" [editable ]="true" required >
</igx-column >
<igx-column field ="Phone" header ="Phone" dataType ="string" [editable ]="true" required phoneFormat ="^[^a-zA-Z]*$" >
<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 >
<igx-column field ="Title" header ="Title" dataType ="string" [editable ]="true" required > </igx-column >
<igx-column field ="HireDate" header ="Hire Date" dataType ="date" [editable ]="true" required >
<ng-template igxCellValidationError let-cell ="cell" let-defaultErr ="defaultErrorTemplate" >
<ng-container *ngTemplateOutlet ="defaultErr" >
</ng-container >
<div *ngIf ="cell.validation.errors?.['futureDate']" >
The date cannot be in the future!
</div >
<div *ngIf ="cell.validation.errors?.['pastDate']" >
The date cannot be before 1st Sept 2004
</div >
</ng-template >
</igx-column >
<igx-column field ="OnPTO" header ="On PTO" dataType ="boolean" [editable ]="true" > </igx-column >
</igx-tree-grid >
<div class ="buttons-wrapper" >
<button igxButton [disabled ]="!treeGrid.transactions.canUndo" (click )="undo()" > Undo</button >
<button igxButton [disabled ]="!treeGrid.transactions.canRedo" (click )="redo()" > Redo</button >
<button igxButton [disabled ]="treeGrid.transactions.getAggregatedChanges(false).length < 1" (click )="commit()" > Commit</button >
</div >
</div >
html コピー .top-row , .grid__wrapper {
margin : 15px ;
}
.buttons-wrapper {
display : flex;
flex-direction : row;
justify-content : left;
padding : 10px 0 ;
}
scss コピー
クロス フィールド検証
場合によっては、1 つのフィールドの検証がレコード内の別のフィールドの値に依存することがあります。
その場合、カスタム検証を使用して共有 FormGroup
を介してレコード内の値を比較できます。
以下のサンプルは、同じレコードの異なるフィールド間のクロスフィールド検証を示しています。ある人に指定された City が現在設定されている Country にあるかどうか、およびその逆を確認します。また、ある人が雇用されたときにその人が 18 歳かどうかも確認します。
次のコード行はクロス フィールド検証関数を示しています。この関数は上記の比較を含み、関連するエラーを設定します。
private rowValidator(): ValidatorFn {
return (formGroup: FormGroup): ValidationErrors | null => {
let returnObject = {};
const age = formGroup.get('Age' );
const hireDate = formGroup.get('HireDate' );
if ((new Date ().getFullYear() - new Date (hireDate.value).getFullYear()) + 18 >= age.value) {
returnObject['ageLessHireDate' ] = true ;
}
const city = formGroup.get('City' );
const country = formGroup.get('Country' );
const validCities = this .countryData.get(country.value);
if (!validCities || !validCities[city.value]) {
returnObject['invalidAddress' ] = true ;
}
return returnObject;
};
}
ts
クロス フィールド検証は、編集モードに入ったときに各行の新しい formGroup
を返す formGroupCreated
イベントから、その行の formGroup
に追加することができます。
<igx-tree-grid igxPreventDocumentScroll #treeGrid [batchEditing ]="true" [data ]="data" primaryKey ="ID"
foreignKey ="ParentID" [width ]="'100%'" [height ]="'500px'" [rowEditable ]="true" [pinning ]="pinningConfig"
(formGroupCreated )="formCreateHandler($event)" >
</igx-tree-grid >
html
public formCreateHandler (evt: IGridFormGroupCreatedEventArgs ) {
evt.formGroup.addValidators(this .rowValidator());
}
typescript
異なるエラーはテンプレート セルに表示され、すべてのエラーは一つのツールチップに結合されます。行の有効状態に応じて、異なるアイコンが表示されます。
<igx-column field ="row_valid" header =" " [editable ]="false" [dataType ]="'number'" [pinned ]="true" [width ]="'150px'" >
<ng-template igxCell let-cell ="cell" >
<div *ngIf ="isRowValid(cell)" [igxTooltipTarget ]="tooltipRef" style ="margin: 'auto';" >
<img width ="18" src ="assets/images/grid/active.png" />
</div >
<div *ngIf ="!isRowValid(cell)" [igxTooltipTarget ]="tooltipRef" style ="margin: 'auto';" >
<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 >
html
各列にはテンプレート化されたフォーム検証があり、カスタム rowValidator
によって行ごとのエラーを確認するため、エラー メッセージ は各セルのエラーを収集する stateMessage
関数で収集されます。
public stateMessage (cell: CellType ) {
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.` );
}
}
});
if (row.validation.errors?.ageLessHireDate) {
messages.push(`\`Age\` cannot be less than 18 when the person was hired.` );
}
if (row.validation.errors?.invalidAddress) {
messages.push(`Selected \`City\` does not match the \`Country\`.` );
}
if (messages.length === 0 && this .isRowValid(cell)) {
messages.push('OK' );
}
return messages;
}
typescript
クロス フィールドの例
以下のサンプルは、クロス フィールド検証の動作を示しています。
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
import {
IgxTreeGridModule,
IgxButtonModule,
IgxTooltipModule
} from "igniteui-angular" ;
import { TreeGridValidatorServiceCrossFieldComponent } from "./tree-grid/tree-grid-validator-service-cross-field/tree-grid-validator-service-cross-field.component" ;
import { ReactiveFormsModule } from "@angular/forms" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
TreeGridValidatorServiceCrossFieldComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxTreeGridModule,
IgxButtonModule,
IgxTooltipModule,
ReactiveFormsModule
],
providers : [],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー
import { Component, OnInit, ViewChild } from '@angular/core' ;
import { FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms' ;
import {
ColumnPinningPosition,
CellType,
IGridEditEventArgs,
IgxTreeGridComponent,
IPinningConfig
} from 'igniteui-angular' ;
import { IGridFormGroupCreatedEventArgs } from 'igniteui-angular/lib/grids/common/grid.interface' ;
import { generateEmployeeDetailedFlatData } from '../data/employees-flat-detailed' ;
@Component ({
selector : 'app-tree-grid-validator-service-cross-field-component' ,
styleUrls : ['tree-grid-validator-service-cross-field.component.scss' ],
templateUrl : 'tree-grid-validator-service-cross-field.component.html'
})
export class TreeGridValidatorServiceCrossFieldComponent implements OnInit {
@ViewChild ('treeGrid' , { static : true })
public treeGrid: IgxTreeGridComponent;
public rowEdit: boolean = false ;
public pinningConfig: IPinningConfig = { columns : ColumnPinningPosition.End };
public data: any [];
public countryData: Map <string , object >;
public countries = [];
public cities = [];
public ngOnInit(): void {
this .data = generateEmployeeDetailedFlatData();
this .countryData = new Map (this .data.map(i => [i.Country, {}]));
this .data.forEach(rec => {
const country = rec.Country;
const city = rec.City;
this .countryData.get(country)[city] = city;
});
this .countries = [...new Set (this .data.map(x => x.Country))];
this .cities = [...new Set (this .data.map(x => x.City))];
}
public editHandler (event: IGridEditEventArgs ) {
if (!event.valid) {
event.cancel = true ;
}
}
public formCreateHandler (evt: IGridFormGroupCreatedEventArgs ) {
evt.formGroup.addValidators(this .rowValidator());
}
private rowValidator(): ValidatorFn {
return (formGroup: FormGroup): ValidationErrors | null => {
let returnObject = {};
const age = formGroup.get('Age' );
const hireDate = formGroup.get('HireDate' );
if ((new Date ().getFullYear() - new Date (hireDate.value).getFullYear()) + 18 >= age.value) {
returnObject['ageLessHireDate' ] = true ;
}
const city = formGroup.get('City' );
const country = formGroup.get('Country' );
const validCities = this .countryData.get(country.value);
if (!validCities || !validCities[city.value]) {
returnObject['invalidAddress' ] = true ;
}
return returnObject;
};
}
public isRowValid (cell: CellType ) {
return !cell.row.validation.errors && !cell.row.cells.some(c => !!c.validation.errors);
}
public stateMessage (cell: CellType ) {
const messages = [];
const cellValidationErrors = cell.row.cells.filter(x => !!x.validation.errors);
cellValidationErrors.forEach(cell => {
const cellErrors = cell.validation.errors;
if (cellErrors?.required) {
messages.push(`The \`${cell.column.header} \` column is required.` );
}
if (cellErrors?.min) {
messages.push(`A value of at least ${cellErrors.min.min} should be entered for \`${cell.column.header} \` column.` );
}
if (cellErrors?.max) {
messages.push(`A value of at maximum ${cellErrors.max.max} should be entered for \`${cell.column.header} \` column.` );
}
});
const rowErrors = cell.row.validation.errors;
if (rowErrors?.ageLessHireDate) {
messages.push(`\`Age\` cannot be less than 18 when the person was hired.` );
}
if (rowErrors?.invalidAddress) {
messages.push(`Selected \`City\` does not match the \`Country\`.` );
}
if (messages.length === 0 && this .isRowValid(cell)) {
messages.push('OK' );
}
return messages;
}
public commit ( ) {
const invalidTransactions = this .treeGrid.validation.getInvalid();
if (invalidTransactions.length > 0 && !confirm('You\'re committing invalid transactions. Are you sure?' )) {
return ;
}
this .treeGrid.transactions.commit(this .data);
this .treeGrid.validation.clear();
}
}
ts コピー <div class ="top-row" >
<igx-switch [(ngModel )]="rowEdit" > Row edit</igx-switch >
</div >
<div class ="grid__wrapper" >
<igx-tree-grid [igxPreventDocumentScroll ]="true" #treeGrid [batchEditing ]="true" [data ]="data" primaryKey ="ID"
foreignKey ="ParentID" [width ]="'100%'" [height ]="'500px'" [rowEditable ]="true" [pinning ]="pinningConfig"
(formGroupCreated )="formCreateHandler($event)" (cellEdit )="editHandler($event)" (rowEdit )="editHandler($event)" >
<igx-column field ="ID" header ="ID" dataType ="number" [editable ]="false" > </igx-column >
<igx-column field ="Age" header ="Age" dataType ="number" [editable ]="true" required min ="18" max ="99" > </igx-column >
<igx-column field ="Name" header ="Full Name" dataType ="string" [editable ]="true" required > </igx-column >
<igx-column field ="City" header ="City" dataType ="string" [editable ]="true" required >
<ng-template igxCellEditor let-cell ="cell" let-fc ='formControl' >
<igx-select [formControl ]="fc" [igxFocus ]="true" >
<igx-select-item *ngFor ="let city of cities" [value ]="city" >
{{ city }}
</igx-select-item >
</igx-select >
</ng-template >
</igx-column >
<igx-column field ="Country" header ="Country" dataType ="string" [editable ]="true" required >
<ng-template igxCellEditor let-cell ="cell" let-fc ='formControl' >
<igx-select [formControl ]="fc" [igxFocus ]="true" >
<igx-select-item *ngFor ="let country of countries" [value ]="country" >
{{ country }}
</igx-select-item >
</igx-select >
</ng-template >
</igx-column >
<igx-column field ="Title" header ="Title" dataType ="string" [editable ]="true" required > </igx-column >
<igx-column field ="HireDate" header ="Hire Date" dataType ="date" [editable ]="true" required > </igx-column >
<igx-column field ="OnPTO" header ="On PTO" dataType ="boolean" [editable ]="true" > </igx-column >
<igx-column field ="row_valid" header =" " [editable ]="false" [pinned ]="true" [width ]="'150px'" >
<ng-template igxCell let-cell ="cell" >
<div *ngIf ="isRowValid(cell)" [igxTooltipTarget ]="tooltipRef" style ="margin: 'auto';" >
<img width ="18" src ="https://www.infragistics.com/angular-demos-lob/assets/images/grid/active.png" />
</div >
<div *ngIf ="!isRowValid(cell)" [igxTooltipTarget ]="tooltipRef" style ="margin: 'auto';" >
<img width ="18" src ="https://www.infragistics.com/angular-demos-lob/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 >
</igx-tree-grid >
<div class ="buttons-wrapper" >
<button igxButton [disabled ]="treeGrid.transactions.getAggregatedChanges(false).length < 1" (click )="commit()" > Commit</button >
</div >
</div >
html コピー .top-row , .grid__wrapper {
padding : 16px ;
padding-bottom : 0 ;
}
.buttons-wrapper {
display : flex;
flex-direction : row;
justify-content : start;
padding : 10px 0 ;
}
scss コピー
スタイル設定
Ignite UI for Angular テーマ ライブラリ を使用して、編集時のデフォルトの検証スタイルを変更できます。
以下の例では、検証メッセージの公開されたテンプレートを使用します。ツールチップをポップアウトし、および、検証のデフォルトの外観を変更するためにエラー時の色をオーバーライドします。
また、無効な行をより明確にするために背景のスタイルを設定します。
テーマのインポート
スタイルを設定し、css 変数にアクセスする最も簡単な方法は、app
のグローバル スタイル ファイル (通常 は styles.scss
です) でスタイルを定義することです。
はじめに themes/index
ファイルをインポートすることにより、Ignite UI for Angular Sass フレームワークの強力なツールへアクセスできるようになります。
@use "igniteui-angular/theming" as *;
scss
スタイルを含める
エラーの色を変更するには、css 変数 --igx-error-500
を使用します。
--igx-error-500: 34 , 80% , 63% ;
scss
カスタム テンプレート
デフォルトのエラー テンプレートを変更することで、カスタム クラスとスタイルを設定できます。
<ng-template igxCellValidationError let-cell ='cell' let-defaultErr ='defaultErrorTemplate' >
<div class ="validator-container" >
<ng-container *ngTemplateOutlet ="defaultErr" >
</ng-container >
</div >
</ng-template >
html
無効な行とセルのスタイル
行とセルは、開発者が行またはセルが無効かどうか、およびアクティブなエラーの種類を知るための API を提供します。
public rowStyles = {
background : (row: RowType ) => row.cells.find(c => c.validation.errors !== null && c.validation.errors !== undefined ) ? '#FF000033' : '#00000000'
};
public cellStyles = {
'invalid-cell' : (rowData, columnKey ) => {
const pKey = this .treeGrid.primaryKey;
const cell = this .treeGrid.getCellByKey(rowData[pKey], columnKey);
return cell && cell.validation.status === 'INVALID' ;
}
}
ts
<igx-tree-grid [rowStyles ]="rowStyles" >
<igx-column *ngFor ="let c of columns" [field ]="c.field" [dataType ]="c.dataType" [header ]="c.label" [required ]="c.required" [cellClasses ]="cellStyles" >
html
デモ
import { NgModule } from "@angular/core" ;
import { FormsModule } from "@angular/forms" ;
import { BrowserModule } from "@angular/platform-browser" ;
import { BrowserAnimationsModule } from "@angular/platform-browser/animations" ;
import { AppComponent } from "./app.component" ;
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive" ;
import { IgxTreeGridModule } from "igniteui-angular" ;
import { TreeGridValidationStyleComponent } from "./tree-grid-validation-style/tree-grid-validation-style.component" ;
@NgModule ({
bootstrap : [AppComponent],
declarations : [
AppComponent,
TreeGridValidationStyleComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxTreeGridModule
],
providers : [],
entryComponents : [],
schemas : []
})
export class AppModule {}
ts コピー import { Component, OnInit, ViewChild } from '@angular/core' ;
import { IgxTreeGridComponent, RowType } from 'igniteui-angular' ;
import { generateEmployeeFlatData, IEmployee } from '../data/employees-flat' ;
@Component ({
selector : 'app-tree-grid-validation-style' ,
styleUrls : ['tree-grid-validation-style.component.scss' ],
templateUrl : 'tree-grid-validation-style.component.html'
})
export class TreeGridValidationStyleComponent implements OnInit {
@ViewChild ('treeGrid' , { read : IgxTreeGridComponent, static : true }) public treeGrid: IgxTreeGridComponent;
public data: IEmployee[];
public columns: any [];
public selectionMode = 'multiple' ;
public rowStyles = {
background : (row: RowType ) => row.cells.find(c => c.validation.errors !== null && c.validation.errors !== undefined ) ? '#FF000033' : '#00000000'
};
public cellStyles = {
'invalid-cell' : (rowData, columnKey ) => {
const pKey = this .treeGrid.primaryKey;
const cell = this .treeGrid.getCellByKey(rowData[pKey], columnKey);
return cell && cell.validation.status === 'INVALID' ;
}
}
public ngOnInit(): void {
this .data = generateEmployeeFlatData();
this .columns = [
{ field : 'Name' , label : 'Full Name' , dataType : 'string' , required : true },
{ field : 'Age' , label : 'Age' , dataType : 'number' },
{ field : 'Title' , label : 'Title' , dataType : 'string' , required : true },
{ field : 'HireDate' , label : 'Hire Date' , dataType : 'date' , required : true }
];
}
}
ts コピー <div class ="grid__wrapper" >
<igx-tree-grid [igxPreventDocumentScroll ]="true" #treeGrid [data ]="data" primaryKey ="ID" foreignKey ="ParentID" width ="100%" height ="520px"
[rowEditable ]="true" [rowStyles ]="rowStyles" >
<igx-column *ngFor ="let c of columns" [field ]="c.field" [dataType ]="c.dataType" [header ]="c.label" [required ]="c.required" [cellClasses ]="cellStyles" >
<ng-template igxCellValidationError let-cell ='cell' let-defaultErr ='defaultErrorTemplate' >
<div class ="validator-container" >
<ng-container *ngTemplateOutlet ="defaultErr" >
</ng-container >
</div >
</ng-template >
</igx-column >
</igx-tree-grid >
</div >
html コピー @use '../../variables' as *;
.grid__wrapper {
margin : 0 auto;
padding : 16px ;
}
.validator-container {
color : hsl(34 , 80% , 63% );
}
:host ::ng-deep {
--ig-error-500 : 34deg , 80% , 63% ;
.invalid-cell {
box-shadow : inset 0 0 0 0.125rem color($color : error);
}
}
scss コピー
API リファレンス
既知の問題と制限
制限
説明
validationTrigger
が blur の場合、editValue
と検証は、エディターからフォーカスが外れた後にのみトリガーされます。
理由は、これが formControl の updateOn
プロパティを利用しているためです。これにより、formControl が更新され、関連する検証をトリガーするイベントが決定されます。
その他のリソース
コミュニティに参加して新しいアイデアをご提案ください。