I implemented a formArray in Angular to manage the distribution of nominees, with fields for Name and Proportion. The plus button (+) adds a new row, while the (X) button deletes the current row. I am now looking to create validation logic that ensures the total proportion column always adds up to 100% (regardless of the number of nominee names).
Example:
No Name Proportion
1 Andy 100
(Total Proportion: 100%)
----------
Example 2:
No Name Proportion
1 Andy 60
2 Bruce 40
(Total Proportion: 100%)
----------
Example 3:
No Name Proportion
1 Andy 60
2 Bruce 20
3 Ciao 20
(Total Proportion: 100%)
----------
Below is my component.html code:
<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>
// Other form field elements...
<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?.proportionValidator">Total must be 100%.
</mat-error>
</mat-form-field>
// Button elements...
</div>
</div>
</ng-container>
</div>
</div>
</form>
</div> <br>
Below is my component.ts code:
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;
}
// Rest of the component.ts code...
}
}
Now, for the proportion.validator.ts file, you can add the following code:
import { AbstractControl } from '@angular/forms';
export function proportionValidator(control: AbstractControl): { [key: string]: boolean } | null {
let sum = 0;
const controlArr = control.value;
if(controlArr !== undefined && controlArr.length > 0){
controlArr.forEach(item => {
sum += parseInt(item.gratuityProportion);
});
return sum === 100 ? null : { proportionValidator: true };
}