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