I created custom component representing password form control (code below is simplified).
PasswordComponent (html)
<form [formGroup]="passwordForm">
...
<input formControlName="password" type="password">
</form>
PasswordComponent (ts)
...
@Component({
selector: 'password',
templateUrl: './password.component.html',
styleUrls: ['./password.component.css'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PasswordComponent),
multi: true
}]
})
export class PasswordComponent implements ControlValueAccessor {
passwordForm: FormGroup;
onChange = (password: string) => { };
onTouched = () => { };
constructor() {
this.passwordForm = new FormGroup({
...
password: new FormControl('')
});
this.passwordForm.valueChanges.subscribe(data => this.onChange(this.value));
}
get value(): string {
return this.passwordForm.get('password').value;
}
writeValue(password: string): void {
this.passwordForm.get('password').setValue(password);
this.onChange(this.value);
}
registerOnChange(fn: any): void { this.onChange = fn; }
registerOnTouched(fn: any): void { this.onTouched = fn; }
setDisabledState?(isDisabled: boolean): void { }
}
I use it in other components instead of standard input element:
<form [formGroup]="userForm">
...
<password formControlName="password"></password>
</form>
Validators are coming from outer form (they're not defined inside PasswordComponent)
this.userForm = fb.group({
...
password: ['', [Validators.minLength(10), Validators.maxLength(100)]]
});
My question is: how can I get <password>
element validity from inside PasswordComponent? I would like to stylize it based on validity. In other words how can I get validity of userForm's 'password' control from PasswordComponent that represents this control.
<div> is a readonly element, usually there's no point in binding form controls to it. You can also create your own custom components (or directives) that can be bound to form controls—if they implement ControlValueAccessor interface.
Form controls enable accessibility by taking a uniform approach to such features as captions, help text, tabbing and keyboard shortcuts. Internationalization issues are addressed by following the same design principles as in XHTML. All form controls are suitable for styling using Aural CSS (ACSS) style properties.
Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM.
As we can't get NgControl
instance directly from DI system since we'll get a circular dependency error. The following diagram shows why it happens if we inject NgControl
in our custom value accessor:
Now it should be clear that we have NgControl -> FormControlName -> ValueAccessor -> CustomValueAccessor -> NgControl
circular dependency
To work around it you can leverageInjector
to achieve that:
component.ts
import { NgControl } from '@angular/forms';
export class PasswordComponent implements ControlValueAccessor {
...
ngControl: NgControl;
constructor(private inj: Injector) {
...
}
ngOnInit() {
this.ngControl = this.inj.get(NgControl)
}
template.html
{{ ngControl.control.valid }}
Plunker Example
One more way to solve this problem is to Remove the NG_VALUE_ACCESSOR from the provider and just inject NgControl. Using the NgControl instance the component will be registered as ValueAccessor.
constructor(
...,
@Optional() @Self() public ngControl: NgControl,
...,
) {
// Setting the value accessor directly (instead of using
// the providers) to avoid running into a circular import.
if (this.ngControl != null) { this.ngControl.valueAccessor = this; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With