Home > Mobile >  Custom input component has required error even after being filled
Custom input component has required error even after being filled

Time:12-11

I've created a custom input component by implementing the ControlValueAccessor interface. Then, I put it on a form with required validation by setting the required property to true. However, it's giving me a "required" error even when the input is filled - in other words, model.errors.invalid is true even after I fill the field.

A reproducible example is available on StackBlitz, but here is some of the code.

Component

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input-password',
  templateUrl: 'input-password.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputPasswordComponent),
      multi: true,
    },
  ],
})
export class InputPasswordComponent implements ControlValueAccessor {
  onChange = (value: any) => {};
  onTouched = () => {};

  private _password = '';
  private passwordIsVisible = false;

  get password() {
    return this._password;
  }

  set password(newPassword: string) {
    this._password = newPassword;
  }

  get buttonLabel() {
    return this.passwordIsVisible ? 'Hide password' : 'Show password';
  }

  get inputType() {
    return this.passwordIsVisible ? 'text' : 'password';
  }

  togglePasswordVisibility() {
    this.passwordIsVisible = !this.passwordIsVisible;
  }

  writeValue(value: any) {
    this.password = value;
    this.onChange(this.password);
  }

  registerOnChange(fn) {
    this.onChange = fn;
  }

  registerOnTouched(fn) {
    this.onTouched = fn;
  }
}

Template

<input [type]="inputType" [(ngModel)]="password" (blur)="onTouched()" />
<button (click)="togglePasswordVisibility()" style="cursor: pointer">
  {{ buttonLabel }}
</button>

Form

<app-input-password
  ngModel
  #model="ngModel"
  [required]="true"
></app-input-password>

<p>Touched? {{ model.touched }}</p>
<p>Required error? {{ model.errors.required }}</p>

CodePudding user response:

You need to place the onChange in the setter instead of of writeValue, that should solve your issue as now the new value will actually be set, it didnt before and thus remained as required (as no value). Also remember to use optional chaining in displaying your error, otherwise it will throw a browser console error.

So remove the onChange from writeValue and set it in the setter:

set password(newPassword: string) {
  this._password = newPassword;
  this.onChange(newPassword);
}  

The optional chaining:

<p>Required error? {{ model?.errors?.required }}</p>

Your forked STACKBLITZ

  • Related