Reputation: 2475
Reactive Form having dynamic patten validation is always invalid on first load. Even after I edit it still invalid.
This is happening only when I am adding dynamic pattern matching validation.
Here is my use case :
Refer this stackblitz
Below is code
<form [formGroup]="data_form">
<table class="table table-border">
<thead>
<th>
name
</th>
<th>
age
</th>
<th><button class="btn btn-primary ">Save</button></th>
</thead>
<tbody>
<ng-container *ngFor='let item of data;let j = index'>
<tr>
<ng-container *ngFor="let td of keys;let i = index">
<ng-container>
<td *ngIf="td !=='isEditable'">
{{item[td]}}
<input [formControlName]="getControlName(j,i)" *ngIf="item.isEditable" type="text" name="" id="">
</td>
</ng-container>
</ng-container>
<td>
<button (click)="item.isEditable = true"> Edit</button>
</td>
</tr>
</ng-container>
</tbody>
</table>
</form>
ts code :
import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
name = "Angular";
data_form = new FormGroup({});
public data;
keys;
ngOnInit() {
this.data = [
{
name: "Sachin",
age: 27,
isEditable: false
},
{
name: "Gopal",
age: 27,
isEditable: false
},
{
name: "Pankaj",
age: 24,
isEditable: false
}
];
this.keys = Object.keys(this.data[0]);
this.data.forEach((element, j) => {
this.keys.forEach((k, i) => {
this.data_form.addControl(
"name_" + j + "_" + i,
new FormControl(element[k], [Validators.required, Validators.pattern(/^[.\d]+$/)])
);
});
});
}
log() {
console.log(this.data);
}
getControlName(j, i) {
return "name_" + j + "_" + i;
}
}
Thanks in Advance.
EDIT:
Various Patterns that I have used :
Validators.pattern("^[a-zA-Z0-9 _/]+$")
Validators.pattern(/^[.\d]+$/)
Validators.pattern(/^(yes|no)$/i)
Validators.pattern(/^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/)
Upvotes: 2
Views: 740
Reputation: 15083
The problem is actually because the pattern is being used for all the controls. I have refactored your code using FormArray
and it seems your pattern work fine
Below is my approach TS File
constructor(private fb: FormBuilder) {}
patterns = [
/^[.\d]+$/,
/^(yes|no)$/i,
/^[a-zA-Z0-9 _/]+$/,
/^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/
];
data = [
{
name: "Sachin",
age: 27,
isEditable: false
},
{
name: "Gopal",
age: 27,
isEditable: false
},
{
name: "Pankaj",
age: 24,
isEditable: false
}
];
keys = [...new Set(this.data.map(item => Object.keys(item)).flat())];
keyPattern = this.keys.map(item => ({
key: item,
pattern: this.patterns.find(pattern =>
this.data.every(i => pattern.test(i[item]))
)
}));
data_form = this.fb.group({
persons: this.fb.array(
this.data.map(item =>
this.fb.group(
this.keyPattern.reduce(
(prev, { key, pattern }) => ({
...prev,
[key]: [
item[key],
[Validators.required, Validators.pattern(pattern)]
]
}),
{}
)
)
)
)
});
get persons(): FormArray {
return this.data_form.get("persons") as FormArray;
}
toggleEdit(j) {
const currentEditStatus = this.persons.controls[j].get("isEditable").value;
this.persons.controls[j].get("isEditable").setValue(!currentEditStatus);
}
HTML
<form [formGroup]="data_form">
<table class="table table-border">
<thead>
<tr>
<th> name </th>
<th>age </th>
<th><button class="btn btn-primary ">Save</button></th>
</tr>
</thead>
<tbody formArrayName='persons'>
<ng-container *ngFor='let item of persons.controls;let j = index'>
<tr [formGroupName]='j'>
<ng-container *ngIf="!item.value.isEditable; else editable">
<td>{{ item.value.name }}</td>
<td>{{ item.value.age }}</td>
</ng-container>
<ng-template #editable>
<td><input formControlName='name'></td>
<td><input formControlName='age'></td>
</ng-template>
<td>
<button (click)="toggleEdit(j)">
{{ !item.value.isEditable ? "Edit": "Cancel"}}
</button>
</td>
</tr>
</ng-container>
</tbody>
</table>
</form>
See below fork Demo
Upvotes: 1