Angular 그리드 편집 및 검증
그리드의 편집은 셀/행을 편집할 때 사용자 입력에 대한 내장된 검증 메커니즘을 노출합니다. 잘 알려진 기능과 더 쉽게 통합할 수 있도록 Angular Form 검증 기능을 확장합니다. 편집기 상태가 변경되면 편집된 셀에 시각적 표시기가 적용됩니다.
구성
일부 Angular Forms 검증자 지시문을 확장하여 IgxColumn
과 직접 작업합니다. 동일한 검증자는 igx-column
에서 선언적으로 설정할 수 있는 속성으로 사용할 수 있습니다. 다음 검증자는 기본적으로 지원됩니다.
필수의
분
최대
이메일
최소 길이
최대 길이
무늬
열 입력이 설정되고 값이 이메일 형식으로 지정되는지 확인하려면 관련 지시문을 사용할 수 있습니다.
<igx-column [field ]="email" [header ]="User E-mail" required email > </igx-column >
html
다음 샘플은 그리드에서 사전 구축된 required
, email
및 min
유효성 검사기 지시문을 사용하는 방법을 보여줍니다.
import { Component } from '@angular/core' ;
import { employeesData } from '../../data/employeesData' ;
import { IgxSwitchComponent, IgxGridComponent, IgxColumnComponent, IgxCellTemplateDirective, IgxAvatarComponent, IgxColumnRequiredValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMinValidatorDirective } from 'igniteui-angular' ;
import { FormsModule } from '@angular/forms' ;
import { DatePipe } from '@angular/common' ;
@Component ({
selector : 'app-grid-validator-service' ,
styleUrls : ['./grid-validator-service.component.scss' ],
templateUrl : './grid-validator-service.component.html' ,
imports : [IgxSwitchComponent, FormsModule, IgxGridComponent, IgxColumnComponent, IgxCellTemplateDirective, IgxAvatarComponent, IgxColumnRequiredValidatorDirective, IgxColumnEmailValidatorDirective, IgxColumnMinValidatorDirective, DatePipe]
})
export class GridValidatorServiceComponent {
public data: any [];
public employeesData: any [];
public rowEdit: boolean = true ;
constructor ( ) {
this .data = employeesData;
}
}
ts コピー <div class ="top-row" >
<igx-switch [(ngModel )]="rowEdit" > Row edit</igx-switch >
</div >
<div class ="grid-wrapper" >
<igx-grid #grid1 [data ]="data" [width ]="'100%'" [height ]="'480px'" [autoGenerate ]="false" [batchEditing ]="true"
[rowEditable ]="rowEdit" [primaryKey ]="'id'" >
<igx-column field ="Avatar" header ="Photo" dataType ="string" width ="80" [editable ]="false" >
<ng-template igxCell let-cell ="cell" >
<div >
<igx-avatar [src ]="cell.row.data.avatar" shape ="circle" size ="small" > </igx-avatar >
</div >
</ng-template >
</igx-column >
<igx-column field ="name" header ="Name" [editable ]="true" required > </igx-column >
<igx-column field ="company" header ="Company" [editable ]="true" > </igx-column >
<igx-column field ="email" width ="190" header ="Email" [editable ]="true" required email > </igx-column >
<igx-column field ="fax" header ="Phone" [editable ]="true" > </igx-column >
<igx-column field ="created_on" header ="Date of Registration" width ="170" [editable ]="true"
[dataType ]="'date'" required >
<ng-template igxCell let-cell >
{{ cell | date: 'longDate' }}
</ng-template >
</igx-column >
<igx-column field ="last_activity" header ="Last Active" width ="170" [editable ]="true" [dataType ]="'date'"
required >
<ng-template igxCell let-cell >
{{ cell | date: 'longDate' }}
</ng-template >
</igx-column >
<igx-column field ="estimated_sales" header ="Estimated Sales" [editable ]="true" [dataType ]="'number'" required min ="0" > </igx-column >
<igx-column field ="deals_lost" header ="Deals Lost" [editable ]="true" [dataType ]="'number'" required min ="0" >
</igx-column >
<igx-column field ="deals_won" header ="Deals Won" [editable ]="true" [dataType ]="'number'" required min ="0" >
</igx-column >
<igx-column field ="deals_pending" header ="Deals Pending" [editable ]="true" [dataType ]="'number'" required
min ="0" > </igx-column >
</igx-grid >
</div >
html コピー .top-row ,
.grid-wrapper {
padding : 16px ;
}
.grid-wrapper {
margin : 0 auto;
padding : 16px ;
}
scss コピー
이 샘플이 마음에 드시나요? 전체 Ignite UI for Angular 툴킷에 액세스하고 몇 분 안에 나만의 앱을 구축해 보세요. 무료로 다운로드하세요.
formGroupCreated
이벤트를 통해 행/셀에서 편집을 시작할 때 유효성 검사에 사용될 FormGroup
노출합니다. 관련 필드에 대한 자체 유효성 검사기를 추가하여 수정할 수 있습니다.
<igx-grid (formGroupCreated )='formCreateHandler($event)' ... >
html
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());
}
ts
고유한 검증 함수를 작성하거나 기본 제공 Angular 검증 함수 중 하나를 사용할 수 있습니다.
검증 서비스 API
그리드는 validation
속성을 통해 유효성 검사 서비스를 노출합니다. 해당 서비스에는 다음과 같은 공개 API가 있습니다.
잘못된 상태는 유효성 검사 규칙에 따라 유효성 검사 오류가 수정되거나 지워질 때까지 유지됩니다.
검증 트리거
유효성 검사는 다음 시나리오에서 트리거됩니다.
참고: 사용자 입력이나 편집 API를 통해 편집되지 않은 레코드에 대해서는 유효성 검사가 트리거되지 않습니다. 셀의 시각적 표시기는 사용자 상호 작용이나 유효성 검사 서비스의 markAsTouched
API를 통해 관련 입력이 터치된 것으로 간주되는 경우에만 표시됩니다.
Angular 그리드 검증 사용자 정의 옵션
사용자 정의 유효성 검사기 설정
템플릿의 <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
앱 모듈에 정의되고 추가되면 그리드의 지정된 열에 선언적으로 설정할 수 있습니다.
<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
속성을 가지며 이에 따라 취소될 수 있습니다. 사용 방법은 Cross-field Validation 예제 에서 확인할 수 있습니다.
<igx-grid (cellEdit )='cellEdit($event)' ... >
html
public cellEdit (evt ) {
if (!evt.valid) {
evt.cancel = true ;
}
}
ts
예
아래 예에서는 위에서 언급한 사용자 정의 옵션을 보여줍니다.
import { Component, Directive, Input, ViewChild } from '@angular/core' ;
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms' ;
import { IgxGridComponent, IgxColumnComponent, IgxCellTemplateDirective, IgxAvatarComponent, IgxColumnRequiredValidatorDirective, IgxColumnEmailValidatorDirective, IgxCellValidationErrorDirective, IgxColumnMinValidatorDirective, IgxButtonDirective } from 'igniteui-angular' ;
import { IGridFormGroupCreatedEventArgs } from 'igniteui-angular/lib/grids/common/grid.interface' ;
import { employeesData } from '../../data/employeesData' ;
import { NgTemplateOutlet, NgIf, DatePipe } from '@angular/common' ;
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 : 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 ;
}
}
@Component ({
selector : 'app-grid-validator-service-extended' ,
styleUrls : ['./grid-validator-service-extended.component.scss' ],
templateUrl : './grid-validator-service-extended.component.html' ,
imports : [IgxGridComponent, IgxColumnComponent, IgxCellTemplateDirective, IgxAvatarComponent, IgxColumnRequiredValidatorDirective, IgxColumnEmailValidatorDirective, PhoneFormatDirective, IgxCellValidationErrorDirective, NgTemplateOutlet, NgIf, IgxColumnMinValidatorDirective, IgxButtonDirective, DatePipe]
})
export class GridValidatorServiceExtendedComponent {
@ViewChild ('grid1' , { read : IgxGridComponent })
public grid: IgxGridComponent;
public data = employeesData;
public formCreateHandler (formGroupArgs: IGridFormGroupCreatedEventArgs ) {
const createdOnRecord = formGroupArgs.formGroup.get('created_on' );
const lastActiveRecord = formGroupArgs.formGroup.get('last_activity' );
createdOnRecord.addValidators(this .futureDateValidator());
lastActiveRecord.addValidators([this .pastDateValidator(), this .futureDateValidator()]);
}
public commit ( ) {
const invalidTransactions = this .grid.validation.getInvalid();
if (invalidTransactions.length > 0 && !confirm('You\'re committing invalid transactions. Are you sure?' )) {
return ;
}
this .grid.transactions.commit(this .data);
this .grid.validation.clear();
}
public undo ( ) {
this .grid.endEdit(true );
this .grid.transactions.undo();
}
public redo ( ) {
this .grid.endEdit(true );
this .grid.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 ('Nov 5 2010' );
if (pastDate){
return pastDate < date ? null : { pastDate : { value : control.value } }
} else return null ;
}
}
}
ts コピー <div class ="grid-wrapper" >
<igx-grid #grid1 [data ]="data" [width ]="'100%'" [height ]="'480px'" [autoGenerate ]="false" [batchEditing ]="true"
[primaryKey ]="'id'" (formGroupCreated )='formCreateHandler($event)' >
<igx-column field ="Avatar" header ="Photo" dataType ="string" width ="80" [editable ]="false" >
<ng-template igxCell let-cell ="cell" >
<div class ="cell__inner avatar-cell" >
<igx-avatar [src ]="cell.row.data.avatar" shape ="circle" size ="small" > </igx-avatar >
</div >
</ng-template >
</igx-column >
<igx-column field ="name" header ="Name" [editable ]="true" required > </igx-column >
<igx-column field ="company" header ="Company" [editable ]="true" > </igx-column >
<igx-column field ="email" width ="190" header ="Email" [editable ]="true" required email > </igx-column >
<igx-column field ="fax" header ="Phone" [editable ]="true" phoneFormat ="\+\d{1}\-(?!0)(\d{3})\-(\d{3})\-(\d{4})\b" >
<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 ="created_on" header ="Date of Registration" width ="170" [editable ]="true" [dataType ]="'date'" 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 >
</ng-template >
<ng-template igxCell let-cell >
{{ cell | date: 'longDate' }}
</ng-template >
</igx-column >
<igx-column field ="last_activity" header ="Last Active" width ="170" [editable ]="true" [dataType ]="'date'" required >
<ng-template igxCell let-cell >
{{ cell | date: 'longDate' }}
</ng-template >
<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 the 5th of November 2010
</div >
</ng-template >
</igx-column >
<igx-column field ="estimated_sales" header ="Estimated Sales" [editable ]="true" [dataType ]="'number'" required min ="0" >
</igx-column >
<igx-column field ="deals_lost" header ="Deals Lost" [editable ]="true" [dataType ]="'number'" required min ="0" >
</igx-column >
<igx-column field ="deals_won" header ="Deals Won" [editable ]="true" [dataType ]="'number'" required min ="0" >
</igx-column >
<igx-column field ="deals_pending" header ="Deals Pending" [editable ]="true" [dataType ]="'number'" required min ="0" >
</igx-column >
</igx-grid >
</div >
<div class ="buttons-wrapper" >
<button igxButton [disabled ]="!grid1.transactions.canUndo" (click )="undo()" > Undo</button >
<button igxButton [disabled ]="!grid1.transactions.canRedo" (click )="redo()" > Redo</button >
<button igxButton [disabled ]="grid1.transactions.getAggregatedChanges(false).length < 1" (click )="commit()" > Commit</button >
</div >
html コピー .grid-wrapper {
margin : 0 auto;
padding : 16px ;
}
.buttons-wrapper {
display : flex;
flex-direction : row;
justify-content : left;
padding : 10px 0 ;
}
scss コピー
교차 필드 검증
일부 시나리오에서는 한 필드의 유효성 검사가 레코드의 다른 필드 값에 따라 달라질 수 있습니다. 이 경우 사용자 정의 유효성 검사기를 사용하여 공유 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' );
const curDate = new Date ();
if (new Date (createdOnRecord.value) > curDate) {
returnObject['createdInvalid' ] = true ;
}
if (new Date (lastActiveRecord.value) > curDate) {
returnObject['lastActiveInvalid' ] = true ;
}
if (new Date (createdOnRecord.value) > new Date (lastActiveRecord.value)) {
returnObject['createdLastActiveInvalid' ] = true ;
}
const dealsRatio = this .calculateDealsRatio(winControl.value, loseControl.value);
if (actualSalesControl.value === 0 && dealsRatio > 0 ) {
returnObject['salesZero' ] = true ;
}
if (actualSalesControl.value > 0 && dealsRatio === 0 ) {
returnObject['salesNotZero' ] = true ;
}
return returnObject;
};
}
public calculateDealsRatio (dealsWon, dealsLost ) {
if (dealsLost === 0 ) return dealsWon + 1 ;
return Math .round(dealsWon / dealsLost * 100 ) / 100 ;
}
ts
교차 필드 유효성 검사기를 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 >
html
public formCreateHandler (evt: IGridFormGroupCreatedEventArgs ) {
evt.formGroup.addValidators(this .rowValidator());
}
typescript
다양한 오류는 모든 오류를 단일 도구 설명에 결합하는 템플릿 셀에 표시됩니다. 행의 유효한 상태에 따라 다른 아이콘이 표시됩니다.
<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 >
html
오류 메시지는 각 셀에 대한 오류를 수집하는 stateMessage
함수에서 수집됩니다. 왜냐하면 각 열에는 템플릿 양식 유효성 검사가 있을 수 있고 사용자 정의 rowValidator
에서 오는 행 자체에 대한 오류를 확인할 수 있기 때문입니다.
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?.createdInvalid) {
messages.push(`The \`Date of Registration\` date cannot be in the future.` );
}
return messages;
}
typescript
교차 필드 예
아래 샘플은 실제 크로스 필드 유효성 검사를 보여줍니다.
import { Component, ViewChild } from '@angular/core' ;
import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn, FormsModule } from '@angular/forms' ;
import { CellType, IgxGridComponent, IGridEditEventArgs, IgxSwitchComponent, IgxColumnComponent, IgxCellTemplateDirective, IgxAvatarComponent, IgxColumnRequiredValidatorDirective, IgxColumnEmailValidatorDirective, IgxCellValidationErrorDirective, IgxColumnMinValidatorDirective, IgxTooltipTargetDirective, IgxTooltipDirective, IgxButtonDirective } from 'igniteui-angular' ;
import { IGridFormGroupCreatedEventArgs } from 'igniteui-angular/lib/grids/common/grid.interface' ;
import { employeesData } from '../../data/employeesData' ;
import { NgTemplateOutlet, NgIf, NgFor, DatePipe } from '@angular/common' ;
@Component ({
selector : 'app-grid-validator-service-cross-field' ,
styleUrls : ['./grid-validator-service-cross-field.component.scss' ],
templateUrl : './grid-validator-service-cross-field.component.html' ,
imports : [IgxSwitchComponent, FormsModule, IgxGridComponent, IgxColumnComponent, IgxCellTemplateDirective, IgxAvatarComponent, IgxColumnRequiredValidatorDirective, IgxColumnEmailValidatorDirective, IgxCellValidationErrorDirective, NgTemplateOutlet, NgIf, IgxColumnMinValidatorDirective, IgxTooltipTargetDirective, IgxTooltipDirective, NgFor, IgxButtonDirective, DatePipe]
})
export class GridValidatorServiceCrossFieldComponent {
@ViewChild ('grid1' , { read : IgxGridComponent })
public grid: IgxGridComponent;
public transactionData = JSON .parse(JSON .stringify(employeesData));
public rowEdit: boolean = true ;
public formCreateHandler (evt: IGridFormGroupCreatedEventArgs ) {
const createdOnRecord = evt.formGroup.get('created_on' );
const lastActiveRecord = evt.formGroup.get('last_activity' );
createdOnRecord.addValidators(this .futureDateValidator());
lastActiveRecord.addValidators(this .futureDateValidator());
evt.formGroup.addValidators(this .rowValidator());
}
public editHandler (event: IGridEditEventArgs ) {
if (!event.valid) {
event.cancel = true ;
}
}
public commit ( ) {
const invalidTransactions = this .grid.validation.getInvalid();
if (invalidTransactions.length > 0 && !confirm('You\'re committing invalid transactions. Are you sure?' )) {
return ;
}
this .grid.transactions.commit(this .transactionData);
this .grid.validation.clear();
}
public calculateDealsRatio (dealsWon, dealsLost ) {
if (dealsLost === 0 ) return dealsWon + 1 ;
return Math .round(dealsWon / dealsLost * 100 ) / 100 ;
}
public getDealsRatio (cell: CellType ) {
const dealsWon = cell.row.cells.find(c => c.column.field === 'deals_won' );
const dealsLost = cell.row.cells.find(c => c.column.field === 'deals_lost' );
const dealsWonValue = dealsWon.editValue != null ? dealsWon.editValue : dealsWon.value;
const dealsLostValue = dealsLost.editValue != null ? dealsLost.editValue : dealsLost.value;
return this .calculateDealsRatio(dealsWonValue, dealsLostValue);
}
private futureDateValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const date = control.value;
if (date > new Date ()){
return { beyondThreshold : { value : control.value } };
}
return null ;
}
}
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' );
const curDate = new Date ();
if (new Date (createdOnRecord.value) > curDate) {
returnObject['createdInvalid' ] = true ;
}
if (new Date (lastActiveRecord.value) > curDate) {
returnObject['lastActiveInvalid' ] = true ;
}
if (new Date (createdOnRecord.value) > new Date (lastActiveRecord.value)) {
returnObject['createdLastActiveInvalid' ] = true ;
}
const dealsRatio = this .calculateDealsRatio(winControl.value, loseControl.value);
if (actualSalesControl.value === 0 && dealsRatio > 0 ) {
returnObject['salesZero' ] = true ;
}
if (actualSalesControl.value > 0 && dealsRatio === 0 ) {
returnObject['salesNotZero' ] = 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?.email) {
messages.push(`Please enter a valid email for \`${cell.column.header} \` column.` );
}
});
const rowErrors = cell.row.validation.errors;
if (rowErrors?.createdInvalid) {
messages.push(`The \`Date of Registration\` date cannot be in the future.` );
}
if (rowErrors?.lastActiveInvalid) {
messages.push(`The \`Last Active\` date cannot be in the future.` );
}
if (rowErrors?.createdLastActiveInvalid) {
messages.push(`The \`Date of Registration\` cannot be greater than the \`Last Active\` date.` );
}
if (rowErrors?.salesZero) {
messages.push(`The \`Actual Sales\` cannot be 0 when the deals ratio is greater than 0.` );
}
if (rowErrors?.salesNotZero) {
messages.push(`The \`Actual Sales\` cannot be greater than 0 when the deals ratio is 0.` );
}
if (messages.length === 0 && this .isRowValid(cell)) {
messages.push('OK' );
}
return messages;
}
}
ts コピー <div class ="top-row" >
<igx-switch [(ngModel )]="rowEdit" > Row edit</igx-switch >
</div >
<div class ="grid__wrapper" >
<igx-grid #grid1 [data ]="transactionData" [width ]="'100%'" [height ]="'500px'" [autoGenerate ]="false" [batchEditing ]="true" [rowEditable ]="rowEdit"
[primaryKey ]="'id'" (formGroupCreated )="formCreateHandler($event)" (cellEdit )="editHandler($event)" (rowEdit )="editHandler($event)" >
<igx-column field ="Avatar" header ="Photo" dataType ="string" width ="80" [editable ]="false" >
<ng-template igxCell let-cell ="cell" >
<div class ="cell__inner avatar-cell" >
<igx-avatar [src ]="cell.row.data.avatar" shape ="circle" size ="small" > </igx-avatar >
</div >
</ng-template >
</igx-column >
<igx-column field ="name" header ="Name" [editable ]="true" required > </igx-column >
<igx-column field ="company" header ="Company" [editable ]="true" > </igx-column >
<igx-column field ="country" header ="Country" [editable ]="true" > </igx-column >
<igx-column field ="city" header ="City" [editable ]="true" > </igx-column >
<igx-column field ="email" width ="190" header ="Email" [editable ]="true" required email > </igx-column >
<igx-column field ="created_on" header ="Date of Registration" width ="170" [editable ]="true" [dataType ]="'date'" required >
<ng-template igxCellValidationError let-cell ='cell' let-defaultErr ='defaultErrorTemplate' >
<ng-container *ngTemplateOutlet ="defaultErr" >
</ng-container >
<div *ngIf ="cell.validation.errors?.['beyondThreshold']" >
The date cannot be in the future.
</div >
</ng-template >
<ng-template igxCell let-cell >
{{ cell | date: 'longDate' }}
</ng-template >
</igx-column >
<igx-column field ="last_activity" header ="Last Active" width ="170" [editable ]="true" [dataType ]="'date'" required >
<ng-template igxCell let-cell >
{{ cell | date: 'longDate' }}
</ng-template >
<ng-template igxCellValidationError let-cell ='cell' let-defaultErr ='defaultErrorTemplate' >
<ng-container *ngTemplateOutlet ="defaultErr" >
</ng-container >
<div *ngIf ="cell.validation.errors?.['beyondThreshold']" >
The date cannot be in the future.
</div >
</ng-template >
</igx-column >
<igx-column field ="estimated_sales" header ="Estimated Sales" [editable ]="true" [dataType ]="'number'" required min ="0" > </igx-column >
<igx-column field ="actual_sales" header ="Actual Sales" [editable ]="true" [dataType ]="'number'" required min ="0" > </igx-column >
<igx-column field ="deals_lost" header ="Deals Lost" [editable ]="true" [dataType ]="'number'" required min ="0" > </igx-column >
<igx-column field ="deals_won" header ="Deals Won" [editable ]="true" [dataType ]="'number'" required min ="0" > </igx-column >
<igx-column field ="deals_ratio" header ="Deals Ratio" [editable ]="false" [dataType ]="'number'" >
<ng-template igxCell let-cell ="cell" >
{{ getDealsRatio(cell) }}
</ng-template >
</igx-column >
<igx-column field ="row_valid" header =" " [editable ]="false" [pinned ]="true" [width ]="'50px'" >
<ng-template igxCell let-cell ="cell" >
<div *ngIf ="isRowValid(cell)" [igxTooltipTarget ]="tooltipRef" class ="valid-image" >
<img width ="18" src ="https://www.infragistics.com/angular-demos-lob/assets/images/grid/active.png" />
</div >
<div *ngIf ="!isRowValid(cell)" [igxTooltipTarget ]="tooltipRef" class ="valid-image" >
<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-grid >
<div class ="buttons-wrapper" >
<button igxButton [disabled ]="grid1.transactions.getAggregatedChanges(false).length < 1" (click )="commit()" > Commit</button >
</div >
</div >
html コピー .top-row , .grid__wrapper {
padding : 16px ;
padding-bottom : 0 ;
}
.valid-image {
margin-left : -5px ;
}
.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.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' ;
}
}
ts
<igx-grid [rowStyles ]="rowStyles" >
<igx-column field ="ReorderLevel" header ="ReorderLever" required [cellClasses ]="cellStyles" >
html
데모
import { Component, ViewChild } from '@angular/core' ;
import { DATA } from '../../data/nwindData' ;
import { IgxGridComponent, RowType, IgxColumnComponent, IgxColumnRequiredValidatorDirective, IgxCellValidationErrorDirective } from 'igniteui-angular' ;
import { IgxPreventDocumentScrollDirective } from '../../directives/prevent-scroll.directive' ;
import { NgTemplateOutlet } from '@angular/common' ;
@Component ({
selector : 'app-grid-validation-style' ,
styleUrls : [`grid-validation-style.component.scss` ],
templateUrl : 'grid-validation-style.component.html' ,
imports : [IgxGridComponent, IgxPreventDocumentScrollDirective, IgxColumnComponent, IgxColumnRequiredValidatorDirective, IgxCellValidationErrorDirective, NgTemplateOutlet]
})
export class GridValidationStyleComponent {
@ViewChild ('grid' , { read : IgxGridComponent, static : true }) public grid: IgxGridComponent;
public data: any [];
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' ;
}
}
constructor ( ) {
this .data = DATA;
}
}
ts コピー <div class ="sample-wrapper" >
<igx-grid [igxPreventDocumentScroll ]="true" #grid [data ]="data" [primaryKey ]="'ProductID'" width ="100%" height ="500px"
[rowEditable ]="true" [rowStyles ]="rowStyles" >
<igx-column field ="ProductID" header ="Product ID" > </igx-column >
<igx-column field ="ReorderLevel" header ="ReorderLever" 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-column field ="ProductName" header ="ProductName" [dataType ]="'string'" 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-column field ="UnitsInStock" header ="UnitsInStock" [dataType ]="'number'" 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-grid >
</div >
html コピー @use "layout.scss" ;
@use "igniteui-angular/theming" as *;
igx-grid {
--ig-error-500: 34deg , 80% , 63% ;
}
scss コピー
API 참조
알려진 문제 및 제한 사항
한정
설명
언제validationTrigger
흐릿하다,editValue
유효성 검사는 편집기가 흐려진 후에만 실행됩니다.
이유는 이것이 formControl의updateOn
재산. 이는 formControl이 관련 유효성 검사기를 업데이트하고 트리거하는 이벤트를 결정합니다.
추가 리소스
우리 커뮤니티는 활동적이며 항상 새로운 아이디어를 환영합니다.