My objective is to put all my validation messages in component instead of html file
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?
You can also use the following alongside the original
onValueChanged
method:I wish Nehal's solution worked, but I couldn't get it to. Came up with my solution after working with @AJT_82 code a bit. My need really came from wanting to check passwords as a group and his solution didn't cover that. So, I have included the other peieces I used to create the full solution that worked for me.
I came accross this problem attempting to do validation of password confirmation in angular 4 after discovering that the method I had used in 2 no longer worked the same. The information on angular.io didn't really help much for this as a lot of the information is fragmented across different areas of their documention.
So, to clarify, this follows Angular's reactive form method. I wrote this to validate passwords in an instance where I also had other validation restrictions (password must be between 4 and 24 characters, is required, etc). This method should work just as well for email confirmation with a few small tweaks.
First, in order to compare validators for a group, the html form must have a subgroup identified using the formGroupName=" " identifier. This is within the primary [formGroup] identifier. Compared inputs must be inside this formGroupName labeled element. In my case it is just a div.
You may notice that I have subvalues for formErrors as
These are built in my code as so...
I built it this way as 'password1' and 'password2' hold my formErrors for each field, while 'password' holds my error in the case of a mismatch (where the two entered passwords are not equal).
Here is may onValueChanged() formula. It checks if a field is an instanceof FormGroup. If so, it first checks the custom validation for that field group first (storing them in 'this.formErrors[field][field]' ), then proceeds to handle subfield validations. In the case of it not being an instanceof FieldGroup, validation is handled as per the example on angular.io's guideance doc.
By giving the FieldGroup a subfield with it's own name, we can store the validations on the FieldGroup. Attempting to do this with the regular onValueChange code overwrites the subfields at the line...
Not providing a place to store the FieldGroup validation either overwrites the subfields or doesn't handle the FieldGroup.
Should you need it, this is how the form is built using buildFomr();
and this is the custom validation function...
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 calledchild
.formErrors
that are used in the code should therefore have thechild
in a insidegroup
:Therefore you must remember when you add the validation in the template, you need to use:
Validation messages won't be inside
group
, but just like the other validation messages:Lastly, the modified
onValueChanges
:Finally, a DEMO :)
Plunker
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 ;)