-1

Component code-

    ngOnInit(): void {
        this.form = this.fb.group({
          currentPassword: ['', [Validators.required], [this.matchCurrentPassword]],
          newPassword: ['', [Validators.required, Validators.minLength(6), Validators.maxLength(12)]],
          confirmPassword: ['', [Validators.required]]
        }
          , { validator: this.ConfirmedValidator('newPassword', 'confirmPassword') }
        )
      }
    matchCurrentPassword = (
        control: AbstractControl
      ): Observable<ValidationErrors | ValidationErrors> => {
        return this.userService.matchCurrentPassword(localStorage.getItem("userId"), control.value)
          .pipe
          (tap(x => { console.log("response:", x) }),
            (map((x: any) => { return x.isExecute ? { matches: true } : { matches: false }; }))
          )
      }
ConfirmedValidator(controlName: string, matchingControlName: string) {
    return (formGroup: FormGroup) => {
      const control = formGroup.controls[controlName];
      const matchingControl = formGroup.controls[matchingControlName];
      if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
        return;
      }
      if (control.value !== matchingControl.value) {
        matchingControl.setErrors({ confirmedValidator: true });
      } else {
        matchingControl.setErrors(null);
      }
    }
  }

Html code-

 <mat-form-field appearance="outline" fxFlex="1 1 calc(100% - 10px)"fxFlex.lt-md="1 1 calc(100% - 10px)" fxFlex.lt-sm="100%" fxFlex.xs="100%" class="from-color">
    <mat-label class="label-padding">Enter Current Password</mat-label>
     <input type="password" class="label-padding" type="text" style="-webkit-text-security: disc;"matInput placeholder="Current Password" formControlName="currentPassword" />
   <mat-error *ngIf="currentPassword.errors?.required && currentPassword.touched">Enter current password</mat-error>
  <mat-error *ngIf="currentPassword.errors?.matches==false">Doesn't match</mat-error>
 </mat-form-field>

The validation for matching current password works perfectly & shows error message according to the condition. But its input field remains invalid after that.

I've also tried validating rest of the input fields. But the currentPassword remains invalid & that causes the entire form remaining invalid.

Why is this happening & how to solve this? Does anyone have any idea?

Valkyrie_30
  • 37
  • 2
  • 7
  • Please show your `ConfirmedValidator` function. – JsNgian Sep 02 '21 at 05:03
  • @JsNgian I've edited the question. But, let me inform you, the **ConfirmedValidator** is **working fine & doesn't have any issue** of remaining invalid even after validation. – Valkyrie_30 Sep 02 '21 at 05:06
  • What dose `matchCurrentPassword` do here. what are you matching your password against (`{ return x.isExecute ? { matches: true } : { matches: false }; }`) ? – JsNgian Sep 02 '21 at 05:33
  • @JsNgian The scenario is, if any user wants to **change** his password, he must enter his current password which I'm **passing to the backend** & **matching with the existing(current) password** of that user. If passwords don't match it will show error message. – Valkyrie_30 Sep 02 '21 at 05:37

1 Answers1

1

According to Defining custom validators,

That function takes an Angular control object and returns either null if the control value is valid or a validation error object.

You need to return null for matchCurrentPassword function if the validation is valid.


Solution

And return { matches: true } in matchCurrentPassword function when the validation is failed.

.component.ts

matchCurrentPassword = (
  control: AbstractControl
): Observable<ValidationErrors | null> => {
  let userId = localStorage.getItem('userId');
  return this.userService.matchCurrentPassword(userId, control.value).pipe(
    tap(x => {
      console.log('response:', x);
    }),
    map((x: any) => {
      return x.isExecute ? null : { matches: true };
    })
  );
};

.component.html

<mat-error *ngIf="currentPassword.errors?.matches">Doesn't match</mat-error>

Sample Solution on StackBlitz

Yong Shun
  • 35,286
  • 4
  • 24
  • 46
  • 1
    Thanks. I've few questions here { return x.isExecute ? null : { matches: false } } here, does the **sequence of returning** null & the { matches: false } matter? Also, Observable here should I match the **return sequence** with the **structure sequence**? Lastly, why does **Observable** & **Observable both work**? – Valkyrie_30 Sep 02 '21 at 05:57
  • 1. It depends on your logic, ensure that return `null` for validation success case. 2. This is more like the developer's opinion. In my opinion, as long the return value match with the function's return type that would be fine. 3. I corrected my answer, it should be `Observable`. – Yong Shun Sep 02 '21 at 06:06