I have created a dynamic form field that I add and remove through a callback function on buttons DelBtn()
to remove item and AddBtn()
to add item
Each of this form fields have a value
input
I also have a totalval
field. I expect the sum of values in all the value
field not to exceed the totalval
. If it does we display an a error message and if the value equals we make the reason
form field appear.
Example:
If I have totalValue = 100
. Now lets say I have my first form field value = 90
.
Then I duplicate the form and in the next field set value = 10
then the reason
field should appear because 90 + 10 = 100
. As the totalValue
has been reached the reason
form field should appear and the add button should be disabled .
If in the second attempt if the user tries to enter a value more than 10 then an error message should be shown.
Below is my current code
In my TS File I have
ischecks: boolean = true;
formsArray = [""];
count: number = 0;
totalval: number = 100;
ngOnInit(): void {}
constructor() {}
clickCount(): void {
this.count++;
}
DelBtn = delIndex => this.formsArray.splice(delIndex, 1);
AddBtn = () => this.formsArray.push("");
HTML
<h2> Form</h2>
<pre style="font-weight: bolder;font-family:verdana;margin-left: 35px;">Total Value:{{totalval}} </pre>
<div *ngFor="let i of formsArray; let a = index">
<div>
<form>
<table>
<div>
<label for="fname">Value:</label><br>
<input type="text" id="fname" name="fname" ><br>
<tr>
<td>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px" (click)="DelBtn(a)" ><span class="fa fa-plus"></span>Delete</button>
</td>
</tr>
<tr>
</div>
</table>
</form>
</div>
</div>
<div *ngIf=ischecks style="margin-left:35%">
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px;margin-bottom: 30%;" (click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
https://stackblitz.com/edit/angular-ivy-3pbdwv?file=src%2Fapp%2Fapp.component.html
Note: If you do not understand the question feel free to ask me in comments adn I am working in angular(typescript)
This will be difficult to achieve with basic Javascript
. Below approach uses ReactiveForm
approach
We Follow the below steps
ReactiveFormsModule
to the imports
array of the module@NgModule({
imports:[ ReactiveFormsModule, ... ],
FormBuilder
classconstructor(private fb: FormBuilder) {}
myForm = this.fb.group({
totalVal: [100],
formsArray: this.fb.array([this.fb.control("", { validators: [Validators.required] })]),
reason: ["", [Validators.required]]
}, { validators: [sumMatches] });
We have added a cusom validator sumMatches
. We will use this to check whether the sum of the total value has been matched
function sumMatches(control): ValidationErrors | undefined {
const totalVal = Number(control.get("totalVal").value);
const formsArrayTotal = control
.get("formsArray")
.value.reduce((a, b) => Number(a) + Number(b), 0);
if (formsArrayTotal !== totalVal) {
return {
sumMismatch: true
};
}
return;
}
getter
functions to extract properties from the formGroup
get sumMismatch(): boolean {
return this.myForm.hasError('sumMismatch')
}
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
get formsArray() {
return this.myForm.get("formsArray") as FormArray;
}
get totalVal() {
return this.myForm.get("totalVal") as FormControl;
}
DelBtn = delIndex => this.formsArray.controls.splice(delIndex, 1);
AddBtn = () => this.formsArray.push(this.fb.control(""));
formGroup
in the html<h2> Form</h2>
<span class='totalVal'>Total Value:{{ totalVal.value }}</span>
<form [formGroup]='myForm'>
<ng-container formArrayName='formsArray'>
<table *ngFor="let item of formsArray.controls; let i = index">
<tr>
<td>
<div>
<label [attr.for]="'fname' + i">Value:</label><br>
<input type="number" [formControlName]="i" type="text" [id]="'fname' + i" name="fname" ><br>
</div>
</td>
<td>
<button type="button" class="btn btn-outline-success"
s (click)="DelBtn(i)" ><span class="fa fa-plus"></span>Delete</button></td>
<tr>
</table>
</ng-container>
<div *ngIf='!sumMismatch && arrayFullyFilled'>
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
(click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
<br>
<span class="error" *ngIf="sumMismatch && myForm.touched">Total Value Mismatch</span>
</form>
I have extracted css to own file
.totalVal {
font-weight: bolder;
font-family: verdana;
}
.btn-outline-success {
border-radius: 40px;
margin-left: 50px;
}
.error {
color: red;
}
To understand this we look at how we build our form group. We defined a structure that produces a value in the form
{
totalVal: 100,
formsArray: [''],
reason: ''
}
By defining our form group as this.fb.group({... }, {validators: [ sumMatches ] }
the form group with the above value will be passed to the sumMatches function
In the sumMatches
we will have a something like a formGroup
with the value
{
totalVal: 100,
formsArray: ['50', '20', '10'],
reason: ''
}
In the above we simply extract the 100 from the formGroup
using control.get('totalVal').value
same to formArray
. Since formArray
value will be an array then we can use reduce
function to sum this.. We finally compare this and return null if they match and an Object
if they do not match.
With the above approach, angular reactive forms will update the value of the form valid
status based on what is provided by the user. We can hence leverage this valid
status to update the UI
arrayFullyFilled()
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
The above code tries to find if the user has filled ALL the inputs in the array. In our array we get all the controls, check if some of them have errors and if any has error return false otherwise return true. This is made possible considering that in my formGroup
I had made the formControls
as required
using Validators.required
validation
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.