Posted 26 March 2018, 10:53 am EST
Hi,
We’ve detected two issues tied to the sorting and filtering features and the way the wijmo FlexGrid interacts with the FormArray in a reactive form. I’ve attached a project which demonstrates both of them.
Angular v5.2.3
Wijmo v3.1.0
Let’s say we want to implement a custom validation that prevents a value to appear more than once within a column, i.e., values inserted in a certain column have to be unique.
Using wj-flex-grid with a wj-flex-grid-filter and implementing validation through reactive form:
<form [formGroup]="formGroup">
<wj-flex-grid
#flexGrid
[selectionMode]="'None'"
formArrayName="formArray">
<wj-flex-grid-filter></wj-flex-grid-filter>
<wj-flex-grid-column
[width]="'*'"
[header]="'Header1'"
[binding]="'column1'">
<ng-template
wjFlexGridCellTemplate
[cellType]="'Cell'"
let-item="item"
let-cell="cell">
<ng-container [formGroupName]="cell.row.index">
<input type="text" [(ngModel)]="item.column1" formControlName="column1">
<ng-container *ngIf="formArray.get([(cell.row.index).toString(), 'column1']).invalid">
{{formArray.get([(cell.row.index).toString(), 'column1']).errors | json}}
</ng-container>
</ng-container>
</ng-template>
</wj-flex-grid-column>
<wj-flex-grid-column
[width]="'*'"
[header]="'Header2'"
[binding]="'column2'">
<ng-template
wjFlexGridCellTemplate
[cellType]="'Cell'"
let-item="item"
let-cell="cell">
<ng-container [formGroupName]="cell.row.index">
<input type="text" [(ngModel)]="item.column2" formControlName="column2">
<ng-container *ngIf="formArray.get([(cell.row.index).toString(), 'column2']).invalid">
{{formArray.get([(cell.row.index).toString(), 'column2']).errors | json}}
</ng-container>
</ng-container>
</ng-template>
</wj-flex-grid-column>
</wj-flex-grid>
</form>
Relevant component code:
@ViewChild('flexGrid') public flexGrid: FlexGrid;
public dataList: {column1: string, column2: string}[] = [
{ column1: 'value1', column2: 'w'},
{ column1: 'value2', column2: 'x'},
{ column1: 'value3', column2: 'y'},
{ column1: 'value4', column2: 'z'},
];
public formGroup: FormGroup;
public formArray: FormArray;
public constructor(private formBuilder: FormBuilder) {
this.createForm();
}
ngOnInit() {
this.flexGrid.headersVisibility = HeadersVisibility.Column;
this.flexGrid.allowSorting = true;
this.flexGrid.allowDragging = AllowDragging.None;
this.flexGrid.allowResizing = AllowResizing.None;
this.flexGrid.itemsSource = this.dataList;
}
private createForm() {
this.formGroup = this.formBuilder.group({
formArray : this.formBuilder.array([])
});
this.formArray = this.formGroup.controls['formArray'] as FormArray;
this.dataList.forEach(() => {
const newFormGroup: FormGroup = this.formBuilder.group(
{
column1: [''],
column2: [''],
}
);
this.formArray.push(newFormGroup);
});
}
Using the following custom validator:
public static uniqueValueValidator(formControl: FormControl): ValidationErrors {
if (!formControl.parent || !formControl.parent.parent) {
return null;
}
// Retrieve all values in the first column
const formArrayList: FormArray = formControl.parent.parent as FormArray;
const valueList: string[] = [];
for (const formGroup of formArrayList.controls) {
valueList.push(formGroup.get('column1').value);
}
// Check if unique
const isUnique = valueList.filter((value: string) => value === formControl.value).length === 1;
return isUnique ? null : { uniqueValue: true };
}
1) Sorting
Say we start with four rows and the following values:
- value1
- value2
- value3
- value4
The way sorting works, it starts by replacing the first value with the last (fourth) one. This triggers the validation… but since the fourth value hasn’t changed yet, the first one is considered invalid, since the values match. The same issue arises with the following line.
Here is a step by step of the problem which can be observed while debugging:
-
value1 ----> value4 {error} (because of row 4)
-
value2 value2
-
value3 value3
-
value4 value4
-
value4 value4 {error}
-
value2 ----> value3 {error} (because of row 3)
-
value3 value3
-
value4 value4
-
value4 value4 {error}
-
value3 value3 {error}
-
value3 ----> value2 (no error)
-
value4 value4
-
value4 value4 {error}
-
value3 value3 {error}
-
value2 value2
-
value4 ----> value1 (no error)
Final result:
- value4 {error}
- value3 {error}
- value2
- value1
The obvious workaround would be to trigger an update of all validations using AbstractControl.updateValueAndValidity() once sorting ends. However, FlexGrid’s (sortedColumn) event triggers before any (ngModelChange), rendering it useless for this purpose.
Is this the intended behavior of (sortedColumn)?
Is there some other way to detect when the sorting has finished so that the validations can be refreshed? Or any other possible workarounds?
2) Filter
This one is a bit trickier to explain but should be obvious if you try to filter one of the values out while the validation is active in the attached example.
There are two issues at play here:
The first one is the one described in the sorting example above. The order in which the values are replaced causes some ghost errors to pop up.
The second issue becomes apparent if you check the FormArray values as shown below the table. As I understand it, activating a filter doesn’t hide the affected rows but instead modifies the value of the existing rows as it sees fit and then hides the unnecessary rows starting from the end of the table.
For example:
- value1
- value2
- value3
- value4
If we filter value3 out, although the table shows the correct result…
- value1
- value2
- value4
…the values in the FormArray reveal what happened under the hood:
{ “column1”: “value1”, … }
{ “column1”: “value2”, … }
{ “column1”: “value4”, … }
{ “column1”: “value4”, … }
“value3” was replaced by “value4” in the third row but the old value in row 4 is not modified and thus any validation becomes incorrect, as they are always done against the values currently in the FormArray and those are not correct.
Is this the intended behavior of the flex-grid-filter?
Are there any workarounds we could use other than completely changing the validation as it is currently implemented?
Thank you.