I want to enclose a matInput of angular material within a component to reuse it in other places of my application because I need to manage its internal state to change the input type from text to password and viceversa.
I managed to do it by implementing ControlValueAccessor but the styles of validation errors are not being displayed.
Password field component:
export class PasswordFieldComponent
implements OnInit, ControlValueAccessor {
@ViewChild(DefaultValueAccessor) private valueAccessor: DefaultValueAccessor;
@Input() customClass: string;
@Input() customPlaceholder: string;
@Input() required = true;
hide = true;
constructor() { }
ngOnInit() {
}
private propagateChange = (_: any) => { };
private onChange(event) {
this.propagateChange(event.target.value);
}
private onTouch() { }
registerOnChange(fn: any): void {
this.valueAccessor.registerOnChange(fn);
}
registerOnTouched(fn: any): void {
this.valueAccessor.registerOnTouched(fn);
}
setDisabledState(isDisabled: boolean): void {
this.valueAccessor.setDisabledState(isDisabled);
}
writeValue(value: any): void {
this.valueAccessor.writeValue(value);
}
}
Password field template:
<mat-form-field class="full-width {{ customClass }}">
<input
matInput
ngDefaultControl
placeholder="{{ customPlaceholder }}"
[required]="required"
[type]="hide ? 'password' : 'text'"
(input)="onChange($event)">
<button mat-icon-button matSuffix (click)="hide = !hide" [attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
<mat-icon>{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
The code from my comments is make the "most simple custom form control that has a material input inside". The idea is create custom ErrorStateMatcher that ask about the control itself. So, out inner material input show errors not when it was invalid else when our custom control was invalid
This ErrorStateMatcher need the know about our control, so we are going to create a constructor to inject this control (I inject in constructor another object "errors" to alow make "invalid" the material input)
The .html is like
The most important part is this
See that use [ngModel] and (ngModel), (blur) mark the custom formControl "touched". I add a mat-error *ngIf="errors?.errorMatch. This is a
@Input()
that get the value of error of Form. This is because we are make a FormGroup that has an custom error if the two fields "password" and "repeatpassword" not match.Our custom form control is like
See how get the ngControl in the ngAfterViewInit, how errorMatcher() return a new CustomFieldErrorMatcher and how pass the values of "control" and "errors".
Well our app.component is like
The .html of the app.component is
The stackblitz
added this listener on the custom component. You can also do it 'blur' event.
https://stackoverflow.com/a/59086644/12425844