1

I am trying to make a custom validator for password but I don't get why I get the following error ERROR TypeError: Cannot read property 'f' of undefined

Below is my code:

registerForm: FormGroup;
submitted = false;
constructor(private Auth:AuthService, private router:Router,private 
formBuilder:FormBuilder) { }

ngOnInit() {
this.registerForm = this.formBuilder.group({
  userName: ['', Validators.compose([Validators.required,Validators.minLength(6),Validators.maxLength(30),Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')])],
  email: ['', Validators.compose([Validators.required, Validators.email])],
  password: ['', Validators.compose([Validators.required, Validators.minLength(6)])],
  cpassword: ['', Validators.required,passwordMatch()] //paswordMatch triggers the error
},);
}

get f() { return this.registerForm.controls; }

function  passwordMatch() { 
if(this.f != undefined){
let passwordInput = this.f.password,
   passwordConfirmationInput = this.f.cpassword;
if (passwordInput.value !== passwordConfirmationInput.value) {
 return passwordConfirmationInput.setErrors({notequal: true})
}
else {
   return passwordConfirmationInput.setErrors(null);
}
}
}
Gabriel Costin
  • 112
  • 3
  • 13

2 Answers2

1

this is undefined in your function, therefore you cannot reference it.

What I would add a sub formgroup, that would track both the value of password and confirm password, but if you want to go this route, modify your code as such:

// ...
// see the brackets also around the validators! No need to use Validators.compose
cpassword: ['', [Validators.required, passwordMatch]]
// ...

Then you would access the form group with parent. Also you need to return either null (valid) or an error:

function passwordMatch(input: FormControl) {
  if (input.parent) {
    let passwordInput = input.parent.get('password');
    if (passwordInput.value !== input.value) {
      return { notequal: true }
    }
    else {
      return null;
    }
  }
}

DEMO: StackBlitz

But with this, we need to remember, if user modifies password field after modifying the confirmpassword and the passwords do not match, the form will still be considered valid. Here's a way to avoid this: password and confirm password field validation angular2 reactive forms

AT82
  • 71,416
  • 24
  • 140
  • 167
  • Sorry for late answer I needed to take a break, I will make a subgroup but for password and confirm password but first I want to see this one working but still is not,I updated the method as you said and I got the following error: ERROR Error: Expected validator to return Promise or Observable. – Gabriel Costin Sep 08 '18 at 18:14
  • ok seems I forgot to put a bracket and everything was fine, sorry but I am trying to learn Angular and today spent too much time on PC and I am tired ,Thank you for help. – Gabriel Costin Sep 08 '18 at 18:17
  • No problem Gabriel, glad I could help. Have a good weekend and do some resting :P – AT82 Sep 08 '18 at 18:24
  • Could you please help me a bit more to understand how the nested group will work?Shall I have a different implementation in my html component except let's say: create a
    and inside div to implement form groups for password and cpassword and ofc at the end of div to have another div for showing error, shall I change something except of what I mention above?
    – Gabriel Costin Sep 08 '18 at 18:40
  • easier with showing, this should be quite clear? https://stackblitz.com/edit/angular-69peym?file=src/app/app.component.ts From what I gather from your comment you seem to be totally on the right track :) – AT82 Sep 08 '18 at 18:49
  • How am I supposed to print the errors for the password field for exemple:
    Password is required
    Password must be at least 6 characters
    because right now I get cannot read property of errors of undifined
    – Gabriel Costin Sep 08 '18 at 19:00
  • Ok I got how to acces control from a nested group but something is not working properly, my html implementation is as you said, however method is working fine but actually after I write something in password field and confirm password field, no matter what I get no error even if the password are not the same – Gabriel Costin Sep 08 '18 at 19:25
  • Could you take the stackblitz I created and reproduce the issue there and I'm happy to take a look at it :) – AT82 Sep 09 '18 at 07:57
  • Ok then I will put the whole code of the registration form so I will try to reproduce it there – Gabriel Costin Sep 09 '18 at 07:59
  • Ok take a look now ,https://stackblitz.com/edit/angular-69peym?file=src%2Fapp%2Fapp.component.ts ,Edit: type something in password field, then you see the message password do not match, type something in cpassword field then error is gone – Gabriel Costin Sep 09 '18 at 08:04
  • You need to fork it, I'm seeing the same stackblitz I created. – AT82 Sep 09 '18 at 08:05
  • Ooops sorry I forgot, try now – Gabriel Costin Sep 09 '18 at 08:06
  • That is not an editor view, and still seems to be the same blitz I created :D – AT82 Sep 09 '18 at 08:07
  • If I go to this url it shows me the stackblitz with all updates I've done:https://stackblitz.com/edit/angular-8whvkp – Gabriel Costin Sep 09 '18 at 08:09
  • You are having the error message inside `cp.errors` that won't work, since if the `cpassword` field is valid, i.e is filled, the error message of equal passwords won't show, since the validator is on the formgroup, not the formcontrol: https://stackblitz.com/edit/angular-bjnqf6?file=src/app/app.component.ts – AT82 Sep 09 '18 at 08:25
  • Ah ty, but I remember that I had this way implemented too but probably at thaat time had something else not working...almost everytime I fix a thing and another one broken, tought that way will work too since everything was inside the div of passwords , ty:D – Gabriel Costin Sep 09 '18 at 08:44
0

you can use input .root to get access to the parent of a control (your registerForm) try this, no need for f function

 function  passwordMatch(input: FormControl) { 
  if(input.root != undefined){
  let passwordInput = input.root.controls.password,
  passwordConfirmationInput = input.root.controls.cpassword;
  if (passwordInput.value !== passwordConfirmationInput.value) {
   return passwordConfirmationInput.setErrors({notequal: true})
  }
  else {
    return passwordConfirmationInput.setErrors(null);
  }
 }

...

cpassword: ['', Validators.required, passwordMatch]
Fateh Mohamed
  • 20,445
  • 5
  • 43
  • 52
  • I get an error at compile time that control is not a property of AbstractControl – Gabriel Costin Sep 08 '18 at 15:43
  • try input.root['controls'] instead of .controls – Fateh Mohamed Sep 08 '18 at 15:49
  • I get this error when I type in confirm password field ERROR Error: Expected validator to return Promise or Observable. Is there a problem with my validator method? – Gabriel Costin Sep 08 '18 at 15:53
  • I figured out what was the problem , already had that issue again earlier , but still after all changes as you said I get no compile error but that function is still throwing an error , cannot read password of unfined... – Gabriel Costin Sep 08 '18 at 15:59