Skip to content

Commit 6464b6e

Browse files
authored
Merge branch 'master' into mkirova/expose-excel-filtering-template
2 parents fb4a368 + 23fb0c2 commit 6464b6e

File tree

7 files changed

+146
-9
lines changed

7 files changed

+146
-9
lines changed

projects/igniteui-angular/src/lib/grids/cell.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -871,11 +871,11 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT
871871
*/
872872
public ngOnChanges(changes: SimpleChanges): void {
873873
if (changes.editMode && changes.editMode.currentValue && this.formControl) {
874-
// while in edit mode subscribe to value changes on the current form control and set to editValue
874+
// ensure when values change, form control is forced to be marked as touche.
875875
this.formControl.valueChanges.pipe(takeWhile(x => this.editMode)).subscribe(value => {
876-
this.editValue = value;
877876
this.formControl.markAsTouched();
878877
});
878+
// while in edit mode subscribe to value changes on the current form control and set to editValue
879879
this.formControl.statusChanges.pipe(takeWhile(x => this.editMode)).subscribe(status => {
880880
if (status === 'INVALID' && this.errorTooltip.length > 0) {
881881
this.cdr.detectChanges();
@@ -948,7 +948,6 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT
948948
cell = this.grid.crudService.createCell(this);
949949
}
950950
cell.editValue = val;
951-
this.formControl.setValue(val);
952951
this.grid.gridAPI.update_cell(cell);
953952
this.grid.crudService.endCellEdit();
954953
this.cdr.markForCheck();
@@ -1060,6 +1059,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy, CellT
10601059

10611060
const isTargetErrorIcon = event && event.target && event.target === this.errorIcon?.el.nativeElement
10621061
if (this.isInvalid && !isTargetErrorIcon) {
1062+
this.cdr.detectChanges();
10631063
this.openErrorTooltip();
10641064
this.grid.activeNodeChange.pipe(first()).subscribe(() => {
10651065
this.closeErrorTooltip();

projects/igniteui-angular/src/lib/grids/common/crud.service.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,38 @@ export interface IgxAddRowParent {
9191
export class IgxCell {
9292
public primaryKey: any;
9393
public state: any;
94+
public pendingValue: any;
9495

9596
constructor(
9697
public id,
9798
public rowIndex: number,
9899
public column,
99100
public value: any,
100-
public editValue: any,
101+
public _editValue: any,
101102
public rowData: any,
102103
public grid: GridType) {
103104
this.grid.validation.create(id.rowID, rowData);
104105
}
105106

107+
public get editValue() {
108+
const formControl = this.grid.validation.getFormControl(this.id.rowID, this.column.field);
109+
if (formControl) {
110+
return formControl.value;
111+
}
112+
}
113+
114+
public set editValue(value) {
115+
const formControl = this.grid.validation.getFormControl(this.id.rowID, this.column.field);
116+
117+
if (this.grid.validationTrigger === 'change') {
118+
// in case trigger is change, mark as touched.
119+
formControl.setValue(value);
120+
formControl.markAsTouched();
121+
} else {
122+
this.pendingValue = value;
123+
}
124+
}
125+
106126
public castToNumber(value: any): any {
107127
if (this.column.dataType === 'number' && !this.column.inlineEditorTemplate) {
108128
const v = parseFloat(value);
@@ -201,6 +221,13 @@ export class IgxCellCrudState {
201221
return;
202222
}
203223

224+
const formControl = this.grid.validation.getFormControl(this.cell.id.rowID, this.cell.column.field);
225+
if (this.grid.validationTrigger === 'blur' && this.cell.pendingValue !== undefined) {
226+
// in case trigger is blur, update value if there's a pending one and mark as touched.
227+
formControl.setValue(this.cell.pendingValue);
228+
formControl.markAsTouched();
229+
}
230+
204231
if (this.grid.validationTrigger === 'blur') {
205232
this.grid.tbody.nativeElement.focus({ preventScroll: true });
206233
}
@@ -245,12 +272,12 @@ export class IgxCellCrudState {
245272
const args = this.cell?.createDoneEditEventArgs(newValue, event);
246273

247274
this.cell.value = newValue;
248-
249275
this.grid.cellEditExit.emit(args);
250276
this.endCellEdit();
251277
return args;
252278
}
253279

280+
254281
/** Clears cell editing state */
255282
public endCellEdit() {
256283
this.cell = null;
@@ -647,11 +674,11 @@ export class IgxGridCRUDService extends IgxRowAddCrudState {
647674
return args.cancel;
648675
}
649676
} else {
677+
this.exitCellEdit(event);
650678
if (!this.grid.rowEditable && this.cell) {
651679
const value = this.grid.transactions.getAggregatedValue(this.cell.id.rowID, true) || this.cell.rowData;
652680
this.grid.validation.update(this.cell.id.rowID, value);
653681
}
654-
this.exitCellEdit(event);
655682
}
656683

657684
args = this.updateRow(commit, event);

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4777,6 +4777,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
47774777
};
47784778

47794779
const cell = new IgxCell(id, index, col, rowData[col.field], value, rowData, this as any);
4780+
const formControl = this.validation.getFormControl(cell.id.rowID, cell.column.field);
4781+
formControl.setValue(value);
47804782
this.gridAPI.update_cell(cell);
47814783
this.cdr.detectChanges();
47824784
}

projects/igniteui-angular/src/lib/grids/grid/grid-validation.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class IgxGridValidationService {
3232
const value = resolveNestedPath(data || {}, col.field);
3333
const field = this.getFieldKey(col.field);
3434
const control = new FormControl(value, { updateOn: this.grid.validationTrigger });
35+
control.setValue(value);
3536
control.addValidators(col.validators);
3637
formGroup.addControl(field, control);
3738
}

projects/igniteui-angular/src/lib/grids/grid/grid-validation.spec.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
2-
import { FormGroup, Validators } from '@angular/forms';
2+
import { FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
33
import { By } from '@angular/platform-browser';
44
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
55
import { IgxInputDirective, IgxTooltipTargetDirective, IgxTreeGridComponent, IgxTreeGridModule } from 'igniteui-angular';
@@ -9,6 +9,7 @@ import { configureTestSuite } from '../../test-utils/configure-suite';
99
import { GridFunctions, GridSelectionFunctions } from '../../test-utils/grid-functions.spec';
1010
import {
1111
ForbiddenValidatorDirective,
12+
IgxGridCustomEditorsComponent,
1213
IgxGridValidationTestBaseComponent,
1314
IgxGridValidationTestCustomErrorComponent,
1415
IgxTreeGridValidationTestComponent
@@ -25,10 +26,11 @@ describe('IgxGrid - Validation #grid', () => {
2526
declarations: [
2627
IgxGridValidationTestBaseComponent,
2728
IgxGridValidationTestCustomErrorComponent,
29+
IgxGridCustomEditorsComponent,
2830
IgxTreeGridValidationTestComponent,
2931
ForbiddenValidatorDirective
3032
],
31-
imports: [IgxGridModule, IgxTreeGridModule, NoopAnimationsModule]
33+
imports: [IgxGridModule, IgxTreeGridModule, NoopAnimationsModule, ReactiveFormsModule]
3234
});
3335
}));
3436

@@ -316,6 +318,82 @@ describe('IgxGrid - Validation #grid', () => {
316318
});
317319
});
318320

321+
describe('Custom Editor Templates - ', () => {
322+
let fixture;
323+
324+
beforeEach(fakeAsync(() => {
325+
fixture = TestBed.createComponent(IgxGridCustomEditorsComponent);
326+
fixture.componentInstance.grid.batchEditing = true;
327+
fixture.detectChanges();
328+
}));
329+
330+
it('should trigger validation on change when using custom editor bound via formControl.', () => {
331+
// template bound via formControl
332+
const template = fixture.componentInstance.formControlTemplate;
333+
const grid = fixture.componentInstance.grid as IgxGridComponent;
334+
const col = grid.columns[1];
335+
col.inlineEditorTemplate = template;
336+
fixture.detectChanges();
337+
338+
let cell = grid.gridAPI.get_cell_by_visible_index(1, 1);
339+
UIInteractions.simulateDoubleClickAndSelectEvent(cell.element);
340+
const input = fixture.debugElement.query(By.css('input'));
341+
UIInteractions.clickAndSendInputElementValue(input, 'bob');
342+
fixture.detectChanges();
343+
344+
GridFunctions.verifyCellValid(cell, false);
345+
const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent;
346+
expect(erorrMessage).toEqual(' Entry should be at least 4 character(s) long ');
347+
});
348+
349+
it('should trigger validation on change when using custom editor bound via editValue.', () => {
350+
// template bound via ngModel to editValue
351+
const template = fixture.componentInstance.modelTemplate;
352+
const grid = fixture.componentInstance.grid as IgxGridComponent;
353+
const col = grid.columns[1];
354+
col.inlineEditorTemplate = template;
355+
fixture.detectChanges();
356+
357+
let cell = grid.gridAPI.get_cell_by_visible_index(1, 1);
358+
UIInteractions.simulateDoubleClickAndSelectEvent(cell.element);
359+
const input = fixture.debugElement.query(By.css('input'));
360+
UIInteractions.clickAndSendInputElementValue(input, 'bob');
361+
fixture.detectChanges();
362+
363+
GridFunctions.verifyCellValid(cell, false);
364+
const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent;
365+
expect(erorrMessage).toEqual(' Entry should be at least 4 character(s) long ');
366+
});
367+
368+
it('should trigger validation on blur when using custom editor bound via editValue.', () => {
369+
// template bound via ngModel to editValue
370+
const template = fixture.componentInstance.modelTemplate;
371+
const grid = fixture.componentInstance.grid as IgxGridComponent;
372+
const col = grid.columns[1];
373+
col.inlineEditorTemplate = template;
374+
grid.validationTrigger = 'blur';
375+
fixture.detectChanges();
376+
377+
let cell = grid.gridAPI.get_cell_by_visible_index(1, 1);
378+
UIInteractions.simulateDoubleClickAndSelectEvent(cell.element);
379+
const input = fixture.debugElement.query(By.css('input'));
380+
UIInteractions.clickAndSendInputElementValue(input, 'bob');
381+
fixture.detectChanges();
382+
383+
// invalid value is entered, but no blur has happened yet.
384+
// Hence validation state is still valid.
385+
GridFunctions.verifyCellValid(cell, true);
386+
expect(cell.errorTooltip.length).toBe(0);
387+
388+
// exit edit mode
389+
grid.crudService.endEdit(true);
390+
fixture.detectChanges();
391+
GridFunctions.verifyCellValid(cell, false);
392+
const erorrMessage = cell.errorTooltip.first.elementRef.nativeElement.children[0].textContent;
393+
expect(erorrMessage).toEqual(' Entry should be at least 4 character(s) long ');
394+
});
395+
});
396+
319397
describe('Transactions integration - ', () => {
320398
let fixture;
321399

projects/igniteui-angular/src/lib/test-utils/grid-validation-samples.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, ViewChild, Directive } from '@angular/core';
1+
import { Component, Input, ViewChild, Directive, TemplateRef } from '@angular/core';
22
import { AbstractControl, NG_VALIDATORS, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
33
import { IgxGridComponent, IgxTreeGridComponent } from 'igniteui-angular';
44
import { data } from '../../../../../src/app/shared/data';
@@ -82,6 +82,32 @@ export class IgxGridValidationTestCustomErrorComponent {
8282
@ViewChild('grid', { read: IgxGridComponent, static: true }) public grid: IgxGridComponent;
8383
}
8484

85+
@Component({
86+
template: `
87+
<igx-grid #grid primaryKey="ProductID" [data]="data" [rowEditable]="rowEditable"
88+
[width]="'1200px'" [height]="'800px'">
89+
<igx-column igxAppForbiddenName='bob' minlength="4" maxlength='8' required
90+
*ngFor="let c of columns"
91+
[editable]='true' [sortable]="true" [filterable]="true" [field]="c.field"
92+
[header]="c.field" [width]="c.width" [resizable]='true' [dataType]="c.dataType">
93+
</igx-column>
94+
</igx-grid>
95+
<ng-template #modelTemplate igxCellEditor let-cell="cell">
96+
<input [(ngModel)]="cell.editValue"/>
97+
</ng-template>
98+
<ng-template #formControlTemplate igxCellEditor let-cell="cell" let-fc='formControl'>
99+
<input [formControl]="fc"/>
100+
</ng-template>
101+
`
102+
})
103+
export class IgxGridCustomEditorsComponent extends IgxGridValidationTestCustomErrorComponent {
104+
@ViewChild('modelTemplate', {read: TemplateRef })
105+
public modelTemplate: TemplateRef<any>;
106+
107+
@ViewChild('formControlTemplate', {read: TemplateRef })
108+
public formControlTemplate: TemplateRef<any>;
109+
}
110+
85111
@Component({
86112
template: `
87113
<igx-tree-grid #treeGrid [data]="data" childDataKey="Employees" primaryKey="ID"

src/app/grid-validation/grid-validation.sample.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ <h4>Grid without transactions</h4>
1111
</igx-column>
1212
<igx-column field='ProductID' [editable]='false'></igx-column>
1313
<igx-column field='ProductName' appForbiddenName='bob' required [editable]='true'>
14+
<ng-template igxCellEditor let-cell="cell" let-value let-fc='formControl'>
15+
<input [(ngModel)]="cell.editValue"/>
16+
</ng-template>
1417
<ng-template igxCellValidationError let-cell='cell' let-defaultErr='defaultErrorTemplate'>
1518
<ng-container *ngTemplateOutlet="defaultErr" >
1619
</ng-container>

0 commit comments

Comments
 (0)