0

I have a reactive form and I want to change password and confirm password validators when I change the password value. I´m subscribing to password control valueChanges and setting the validators when the control value changes. To avoid repeating the operation just once, I unsubscribed after setting validators.

The problem is that the validators are set correctly but once all fields get completed, although there is no error in the form, but it is marked as invalid.

I have tried setting the validators outside the subscription to the valueChanges and it works properly.

I tried using a function that returns the controls that have errors but no error was returned once I completed all fields as expected.

I don´t know why it doesn't work properly.

Form code:

 this.form = this.fb.group({
     usuario: [this.data.usuario, [Validators.required]],
     password: [this.data.password, [Validators.required, Validators.minLength(6)]],
     confirmarPassword: [{ value: '', disabled: true }],
     nombre: [this.data.nombre, [Validators.required]],
     apellido: [this.data.apellido, [Validators.required]],
     inicial: [this.data.inicial, [Validators.required]],
     email: [this.data.email, [Validators.required, Validators.email]],
     habilitado: [this.data.habilitado.value],
     imagenPerfil: [this.data.imagenPerfil, null],
     modoNuevoPerfil: [false],
     modoEditarPerfil: [false],
     permisos: this.fb.group({
         nombre: ['', null],
         usuarios: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         usuariosPerfiles: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         configuracionesTecnicas: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         drogas: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         drogasCertificados: this.fb.group({
             alta: [false],
             baja: [false],
             visualizacion: [false]
         }),
         drogasRetesteos: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             aprobarRechazar: [false],
             visualizacion: [false]
         }),
         drogasMovimientos: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         soluciones: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             aprobarRechazar: [false],
             visualizacion: [false]
         }),
         equiposAuxiliares: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         equiposAuxiliaresCertificados: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         materialVolumetrico: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
         materialVolumetricoCertificados: this.fb.group({
             alta: [false],
             baja: [false],
             modificacion: [false],
             visualizacion: [false]
         }),
     })
 });

Value changes subscription code:

this.passwordChange$ = this.form.controls.usuario.valueChanges.subscribe(
    () => {
        this.form.setValidators(this.passwordCoinciden('password', 'confirmarPassword'));
        this.form.controls.confirmarPassword.enable()
        this.form.controls.confirmarPassword.setValidators([Validators.required]);
        this.form.updateValueAndValidity()
        this.passwordChange$.unsubscribe()
    }
)

Function to check errors code:

 function findInvalidControls() {
     const invalid = [];
     const controls = this.form.controls;
     for (const name in controls) {
         if (controls[name].invalid) {
             invalid.push(name);
         }
     }
     return invalid;
 }
3
  • why not forget remove/add validators and use some like confirmarPassword: [value: '', [this.passwordCoinciden('password', 'confirmarPassword'),Validators.required]. Remember that a FormControl disabled is not checked -it's always valid-. BTW, what's the aim of disabled comfirmarPassword? Commented Sep 4, 2020 at 7:45
  • Are you sure your form is still listening after the first changes ? Because you unsubscribe just at the end so it will listen the first time then stop listening and so this code will never be executed again Commented Sep 4, 2020 at 8:24
  • @ Eliseo. This form is inside a dialog that when is opened to add a new user it works properly. When I open it for editing an existing user y load all fetched user data but i do not need to validate the password until it is modified. That is why I disable confirmarPassword. @Lud I have tried both approaches unsubscribing an not unsubscribing. The idea of unsubscribing was that once the validators are set that code is not executed again. Commented Sep 4, 2020 at 10:57

3 Answers 3

1

I wrote a post regarding RF and custom validator functions, you can find some examples

https://dev.to/salimchemes/reactiveforms-formarrays-and-custom-validators-1d0k

Sign up to request clarification or add additional context in comments.

Comments

0

I have found a way to solve this, i do not understand why it works this why but still this could help some one.

Set my cumstom validator when the form is initialized but keep my confirmarPassword disabled so the validation is not checked.

Then I subscribe to this.form.controls.password.valueChanges and when it changes i enable confirmarPassword

So as I said i do not know why but the the form validators do not work as expected if you set them inside a formControl valueChanges observable.

Form Code:

         this.form = this.fb.group({
         usuario: [this.data.usuario, [Validators.required]],
         password: [this.data.password, [Validators.required, Validators.minLength(6)]],
         confirmarPassword: [{ value: '', disabled: true }],
         nombre: [this.data.nombre, [Validators.required]],
         apellido: [this.data.apellido, [Validators.required]],
         inicial: [this.data.inicial, [Validators.required]],
         email: [this.data.email, [Validators.required, Validators.email]],
         habilitado: [this.data.habilitado.value],
         imagenPerfil: [this.data.imagenPerfil, null],
         modoNuevoPerfil: [false],
         modoEditarPerfil: [false],
     });
// Here I set the validator but it does not work until I enable 'confirmarPassword'
    this.form.setValidators(this.passwordCoinciden('password', 'confirmarPassword'));

Value Changes Subscription Code:

this.passwordChange$ = this.form.controls.usuario.valueChanges.subscribe(
    () => {
        this.form.controls.confirmarPassword.setValidators([Validators.required]);
        this.form.controls.confirmarPassword.enable()
        this.form.updateValueAndValidity()
        this.passwordChange$.unsubscribe();
    }
)

UPDATE 9/7/2020

I found that the problem was in my custom validator i was using incorrectly the ValidatorFn interfase because i was returning an empty object when there was no error instead of null.

MODIFIED CUSTOM VALIDATOR CODE

passwordCoinciden(_password: string, _confirmarPassword: string) : ValidatorFn{
return (group: FormGroup): {[key: string]: any} => {
  let password = group.controls[_password];
  let confirmarPassword = group.controls[_confirmarPassword];

  if (password.value && confirmarPassword.value && password.value != confirmarPassword.value ) {
    this.form.controls[_confirmarPassword].markAllAsTouched()
    this.form.controls[_confirmarPassword].setErrors({passNoCoinciden: "Los passwords no coinciden" })
    return {passNoCoinciden: "Los passwords no coinciden" }
           
  } else {
  this.form.controls[_confirmarPassword].setErrors(null);
   return  null
  } 
}

}

Comments

0

here is a working example.

export class AppComponent implements OnInit {
  loginForm: FormGroup = this.fb.group({
    phonenumber: [""],
    check: [false]
  });

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.loginForm.get("check").valueChanges.subscribe(value => {
      if (value) {
        this.loginForm.get("phonenumber").setValidators([Validators.required]);
      } else {
        this.loginForm.get("phonenumber").clearValidators();
      }
      this.loginForm.get("phonenumber").updateValueAndValidity({ emitEvent : false });
      console.log(this.loginForm.valid);
    });
  }

  loginUser() {}
}

you can test it here

For those who get an error saying "MaxExecutionStack..." be aware, that this is because

emitEvent : false

option inside updateValueAndValidity method!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.