I have a sign up page and below is the fields:
public buildRegisterForm() {
this.userForm = this.fb.group({
firstName: ['', [Validators.required, Validators.minLength(3)]],
lastName: ['', [Validators.required, Validators.maxLength(50)]],
emailGroup: this.fb.group({
email: ['', [Validators.required, Validators.pattern(this.emailPattern)]],
retypeEmail: ['', Validators.required],
}, { validator: formMatcherValidator('email', 'retypeEmail') }),
passwordGroup: this.fb.group({
password: ['', [Validators.required, strongPasswordValidator()]],
retypePassword: ['', Validators.required],
}, { validator: formMatcherValidator('password', 'retypePassword')}),
});
}
I'm following this tutorial link to achieve what I want which is
to put all my validation messages in component file instead of html file.
export const validationMessages = {
'firstName': {
'required': 'Your first name is required.',
'minlength': 'Your first name must be at least 3 characters long.'
},
'lastName': {
'required': 'Your last name is required.',
'minlength': 'Your last name must be less than 50 characters long.'
},
'emailGroup': {
'email': {
'required': 'Your email is required',
'pattern': 'Your login email does not seem to be a valid email address.'
},
'retypeEmail': {
'required': 'Your retype email is required',
'match': 'The email provided do not match.'
},
},
'passwordGroup':{
'password': {
'required': 'Your password is required',
'strongPassword': 'Your password must be between 8 - 15 characters and must contain at least three of the following: upper case letter, lower case letter, number, symbol.'
},
'retypePassword': {
'required': 'Your retype password is required',
'match': 'The password provided do not match.'
}
}
onValueChanged method
private onValueChanged(data?: any) {
if (!this.userForm) { return; }
const form = this.userForm;
// tslint:disable-next-line:forin
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
let control = form.get(field);
// console.log("control", control.dirty);
console.log("controlEmail", control);
if (control && (control.dirty || control.touched) && control.invalid) {
let messages = validationMessages[field];
// tslint:disable-next-line:forin
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
And this method is not working when I have multi formBuider Group or nested Object. Any tips for this 1?
similar to this How to validate reactive forms with nested form groups?
The way I see it, you need to create a nested loop inside the onValueChanged(data)
-method. Since you have pretty many nested groups, I'm not going to replicate that. But the nested loop is generic, so it works for all your groups. But here is an example with just one nested group instead of several. I'm using the heroes example.
The nested group name is group
, and the formcontrol inside that is called child
.
formErrors
that are used in the code should therefore have the child
in a inside group
:
formErrors = {
'name': '',
'power': '',
'group':{
'child': ''
}
};
Therefore you must remember when you add the validation in the template, you need to use:
<div *ngIf="formErrors.group.child">
{{ formErrors.group.child }}
</div>
Validation messages won't be inside group
, but just like the other validation messages:
validationMessages = {
'name': {
'required': 'Name is required.',
},
'power': {
'required': 'Power is required.'
},
'child': {
'required': 'Child is required.',
}
};
Lastly, the modified onValueChanges
:
onValueChanged(data?: any) {
if (!this.heroForm) { return; }
const form = this.heroForm;
// iterate toplevel of formErrors
for (const field in this.formErrors) {
// check if the field corresponds a formgroup (controls is present)
if(form.get(field).controls ) {
// if yes, iterate the inner formfields
for(const subfield in form.get(field).controls) {
// in this example corresponds = "child", reset the error messages
this.formErrors[field][subfield] = '';
// now access the actual formfield
const control = form.get(field).controls[subfield];
// validate and show appropriate error message
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[subfield];
for (const key in control.errors) {
this.formErrors[field][subfield] += messages[key] + ' ';
}
}
}
}
// does not contain a nested formgroup, so just iterate like before making changes to this method
else {
const control = form.get(field);
this.formErrors[field] = '';
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
}
Finally, a DEMO :)
You'd have to remember though that in your case this works, but IF there would be nested groups inside the nested groups, this would not work, then you'd have to make yet another loop in the onValueChanges
, but you don't have that problem here ;)