Наконец я решил разделить два условия.см. new stackblitz
ngOnInit() {
this.form = this.fb.group({
distance: this.fb.array([], this.distanceValidator()),
});
this.addRow()
}
addRow() {
const control = this.form.controls.distance as FormArray;
control.push(this.fb.group({
from: ['', Validators.required],
to: ['', Validators.required]
}, { validator: this.greaterValidator() }));
}
setDefault() {
const control = this.form.controls.distance as FormArray;
this.default.forEach(data => {
control.push(this.fb.group({
from: [data.from, Validators.required],
to: [data.to, Validators.required]
}, { validator: this.greaterValidator() }));
});
}
greaterValidator() {
return (fa: FormGroup) => {
return fa.value.to && fa.value.to < fa.value.from ? { error: "from greater than to" } : null;
}
}
distanceValidator() {
return (fa: FormArray) => {
let ok = true;
for (let i = 1; i < fa.value.length; i++) {
ok = (!fa.value[i].from || fa.value[i].from > fa.value[i - 1].to) && (!fa.value[i].to || fa.value[i].to > fa.value[i - 1].from);
if (!ok)
return { error: "from/to yet included", index: i }
}
return null
}
}
И .html
<form [formGroup]="form">
<button (click)="addRow()">Add</button>
<div formArrayName="distance" >
<div
*ngFor="let item of form.get('distance').controls; let i = index"
[formGroupName]="i"
style="display: flex">
<input type="number"
placeholder="From"
formControlName="from">
<div>
<input type="number"
placeholder="To"
formControlName="to">
</div>
<span *ngIf="item.errors">*</span>
<span *ngIf="form.get('distance')?.errors && form.get('distance')?.errors.index==i">**</span>
</div>
</div>
<div *ngIf="form.get('distance')?.errors">{{form.get('distance')?.errors.error}}</div>
<br><br>
<button type="submit" [disabled]="!form.valid"> Submit </button>
</form>
<button (click)="setDefault()"> Set Default Values </button>
Обновление : на самом деле только тогда, когда обнаружена ошибка, больше не контролируется.Более того, если from и to before пустые, не выдают ошибку.Во избежание этого мы можем «преобразовать» в число, написав
let ok = (!fa.value[i].from || fa.value[i].from > +fa.value[i - 1].to)
&& (!fa.value[i].to || fa.value[i].to > +fa.value[i - 1].from);
(см. «+» В + fa.value [i-1] .to и + fa.value [i-1]).из
Итак, поскольку мы решили отправленную нами ошибку, представьте, что у вас есть 6 строк, и строка в позиции 0, в позиции 3 и в позиции 4 (0 - это первая строка) отправляет ошибку наподобие
{error:"there are errors",indexError:",0,3,4,"}
Это позволяет в * ngFor писать что-то вроде
<span *ngIf="form.get('distance')?.errors &&
form.get('distance')?.errors.indexError.indexOf(','+i+',')>=0">
**
</span>
Ну, наш distanceValidator становится похожим на
distanceValidator() {
return (fa: FormArray) => {
let indexError:string="";
for (let i = 1; i < fa.value.length; i++) {
let ok = (!fa.value[i].from || fa.value[i].from > +fa.value[i - 1].to) && (!fa.value[i].to || fa.value[i].to > +fa.value[i - 1].from);
if (!ok)
indexError+=','+i;
}
return indexError?{error:"there are errors",indexError:indexError+','}:null
}
Кто-то может подумать, что лучше вернуть массив ошибок, но это не разрешено, так как легко узнать строку с ошибками. Некоторые, например, errors.find (x => x.id == i) не работают, потому что мы не можем использовать find в интерполяции.
Это правда, что сравнивайте только одну строку с промежуточным значением ранее. Можно проверить все перед тем, как использовать - for (пусть j = i-1; j> 0; j ++) {ok = ok && ...} -, ноЯ думаю, что это не обязательно, и мы должны быть скупы в коде. Помните, что функция distanceValidator выполняется несколько раз. См. Еще stackblitz