Angular FormArray 中的自定义验证(比例总和必须为 100%)
Custom Validations in Angular FormArray (Sum of proportion must be 100%)
我在 angular 中创建了一个 formArray 来表示 Nominee 的分布,它有两个字段 Name 和 Proportion。加号按钮 (+) 添加新行,(X) 按钮删除当前行。现在我想编写验证代码,使比例列的总数必须为 100%(无论它包含多少被提名人的名字)
例如
无姓名比例
1 安迪 100
(比例之和为100)
------------
例 2
无姓名比例
1 安迪 60
2 Bruce 40
(比例之和为100)
----------
例 3
无姓名比例
1 安迪 60
2 布鲁斯 20
3 Ciao 20
(比例之和为100)
----------
这是我的component.html代码
<h5>Nominees</h5>
<div class="row">
<form novalidate [formGroup]="FormNominees">
<div clas="col-xs-12 form-group marL40">
<div formGroupName="itemRows">
<ng-container *ngIf="FormNominees.controls.itemRows!=null">
<div *ngFor="let itemrow of FormNominees.controls.itemRows.controls; let i = index"
[formGroupName]="i">
<div class="row">
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Name" formControlName="name">
<mat-error *ngIf="f9.name.touched && f9.name.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.name.touched && f9.name.errors?.pattern">Can only contain characters.
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Relationship"
formControlName="relationship">
<mat-error *ngIf="f9.relationship.touched && f9.relationship.errors?.required">It is mandatory
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<mat-select formControlName="gender" placeholder="Gender">
<mat-option value="Male">Male</mat-option>
<mat-option value="Female">Female</mat-option>
<mat-option value="Other">Other</mat-option>
</mat-select>
<mat-error *ngIf="f9.gender.touched && f9.gender.errors?.required">It is mandatory
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="phone" formControlName="phone">
<mat-error *ngIf="f9.phone.touched && f9.phone.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.phone.touched && f9.phone.errors?.pattern">Can only contain numbers.
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Proportion"
formControlName="gratuityProportion">
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.pattern">Can only contain numbers.
</mat-error>
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.proportionValidator">Total must be 100%.
</mat-error>
</mat-form-field>
<div class="col-sm-2.4">
<button (click)="deleteRow(i)" class="btn btn-danger">x</button>
<!-- </div>
<div class="form-group"> -->
<button type="button" (click)="addnewRow()"
[disabled]="FormNominees.invalid"
class="btn btn-primary">+</button>
</div>
</div>
</div>
</ng-container>
</div>
</div>
</form>
</div> <br>
这是我的component.ts代码
import { proportionValidator } from './proportion.validator';
@Component({
selector: 'app-component',
templateUrl: './component.component.html',
styleUrls: ['./component.component.scss']
})
export class GratuityComponent implements OnInit {
FormNominees: FormGroup;
TotalRow: number;
itemFB: any;
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.FormNominees = this.fb.group({
itemRows: this.fb.array([this.initItemRow()])
});
initItemRow() {
this.itemFB = this.fb.group({
name: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9. ]*' )]],
relationship: ['', Validators.required],
phone: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9. ]*' )]],
gratuityProportion: ['', [Validators.required, Validators.pattern('^[0-9]*$'), proportionValidator] ],
gender:['', Validators.required],
employeeUniqueId: '00000001-0001-0001-0001-000000000001'
})
return this.itemFB;
}
addnewRow() {
const control = <FormArray>this.FormNominees.controls['itemRows'];
control.push(this.initItemRow())
}
deleteRow(index: number) {
const control = <FormArray>this.FormNominees.controls['itemRows'];
// control.push(this.initItemRow())
if (control != null) {
this.TotalRow = control.value.length;
}
if (this.TotalRow > 1) {
control.removeAt(index);
} else {
alert('One record is mandatory');
return false;
}
}
get f9() {return this.itemFB.controls;}
}
}
现在,我的问题是我应该在proportion.validator.ts中写什么代码来实现这个条件?
const arr = new FormArray(
[
new FormControl('Nancy'),
new FormControl('Drew')
],
{
validators: myValidator //<- this is where your proportion validator goes
}
);
在你的情况下,它看起来像这样:
this.FormNominees = this.fb.group({
itemRows: this.fb.array(
[
this.initItemRow()
],
{
validators: proportionValidator
}
)
});
我在 angular 中创建了一个 formArray 来表示 Nominee 的分布,它有两个字段 Name 和 Proportion。加号按钮 (+) 添加新行,(X) 按钮删除当前行。现在我想编写验证代码,使比例列的总数必须为 100%(无论它包含多少被提名人的名字)
例如
无姓名比例
1 安迪 100
(比例之和为100)
------------
例 2
无姓名比例
1 安迪 60
2 Bruce 40
(比例之和为100)
----------
例 3
无姓名比例
1 安迪 60
2 布鲁斯 20
3 Ciao 20
(比例之和为100)
----------
这是我的component.html代码
<h5>Nominees</h5>
<div class="row">
<form novalidate [formGroup]="FormNominees">
<div clas="col-xs-12 form-group marL40">
<div formGroupName="itemRows">
<ng-container *ngIf="FormNominees.controls.itemRows!=null">
<div *ngFor="let itemrow of FormNominees.controls.itemRows.controls; let i = index"
[formGroupName]="i">
<div class="row">
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Name" formControlName="name">
<mat-error *ngIf="f9.name.touched && f9.name.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.name.touched && f9.name.errors?.pattern">Can only contain characters.
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Relationship"
formControlName="relationship">
<mat-error *ngIf="f9.relationship.touched && f9.relationship.errors?.required">It is mandatory
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<mat-select formControlName="gender" placeholder="Gender">
<mat-option value="Male">Male</mat-option>
<mat-option value="Female">Female</mat-option>
<mat-option value="Other">Other</mat-option>
</mat-select>
<mat-error *ngIf="f9.gender.touched && f9.gender.errors?.required">It is mandatory
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="phone" formControlName="phone">
<mat-error *ngIf="f9.phone.touched && f9.phone.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.phone.touched && f9.phone.errors?.pattern">Can only contain numbers.
</mat-error>
</mat-form-field>
<mat-form-field
class="example-full-width d-block input-small-size col-sm-2.4"
appearance="outline">
<input matInput placeholder="Proportion"
formControlName="gratuityProportion">
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.required">It is mandatory
</mat-error>
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.pattern">Can only contain numbers.
</mat-error>
<mat-error *ngIf="f9.gratuityProportion.touched && f9.gratuityProportion.errors?.proportionValidator">Total must be 100%.
</mat-error>
</mat-form-field>
<div class="col-sm-2.4">
<button (click)="deleteRow(i)" class="btn btn-danger">x</button>
<!-- </div>
<div class="form-group"> -->
<button type="button" (click)="addnewRow()"
[disabled]="FormNominees.invalid"
class="btn btn-primary">+</button>
</div>
</div>
</div>
</ng-container>
</div>
</div>
</form>
</div> <br>
这是我的component.ts代码
import { proportionValidator } from './proportion.validator';
@Component({
selector: 'app-component',
templateUrl: './component.component.html',
styleUrls: ['./component.component.scss']
})
export class GratuityComponent implements OnInit {
FormNominees: FormGroup;
TotalRow: number;
itemFB: any;
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.FormNominees = this.fb.group({
itemRows: this.fb.array([this.initItemRow()])
});
initItemRow() {
this.itemFB = this.fb.group({
name: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9. ]*' )]],
relationship: ['', Validators.required],
phone: ['', [Validators.required, Validators.pattern('[a-zA-Z0-9. ]*' )]],
gratuityProportion: ['', [Validators.required, Validators.pattern('^[0-9]*$'), proportionValidator] ],
gender:['', Validators.required],
employeeUniqueId: '00000001-0001-0001-0001-000000000001'
})
return this.itemFB;
}
addnewRow() {
const control = <FormArray>this.FormNominees.controls['itemRows'];
control.push(this.initItemRow())
}
deleteRow(index: number) {
const control = <FormArray>this.FormNominees.controls['itemRows'];
// control.push(this.initItemRow())
if (control != null) {
this.TotalRow = control.value.length;
}
if (this.TotalRow > 1) {
control.removeAt(index);
} else {
alert('One record is mandatory');
return false;
}
}
get f9() {return this.itemFB.controls;}
}
}
现在,我的问题是我应该在proportion.validator.ts中写什么代码来实现这个条件?
const arr = new FormArray(
[
new FormControl('Nancy'),
new FormControl('Drew')
],
{
validators: myValidator //<- this is where your proportion validator goes
}
);
在你的情况下,它看起来像这样:
this.FormNominees = this.fb.group({
itemRows: this.fb.array(
[
this.initItemRow()
],
{
validators: proportionValidator
}
)
});