I have a custom form control component in my Angular application, which implements ControlValueAccessor
interface.
However, I want to access the FormControl
instance, associated with my component. I'm using reactive forms with FormBuilder
and providing form control using formControlName
attribute.
SO, how do I access FormControl
instance from inside of my custom form component?
To fetch the value of a form control, we have to use value property on the instance of FormControl in our class. In the same way we can fetch the value in HTML template. city = new FormControl('Noida'); console. log(this.
This solution was born from the discussion in the Angular repository. Please, make sure to read it or even better to participate if you are interested in this problem.
I've studied the code of FormControlName
directive and it's inspired me to write the following solution:
@Component({
selector: 'my-custom-form-component',
templateUrl: './custom-form-component.html',
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: CustomFormComponent,
multi: true
}]
})
export class CustomFormComponent implements ControlValueAccessor, OnInit {
@Input() formControlName: string;
private control: AbstractControl;
constructor (
@Optional() @Host() @SkipSelf()
private controlContainer: ControlContainer
) {
}
ngOnInit () {
if (this.controlContainer) {
if (this.formControlName) {
this.control = this.controlContainer.control.get(this.formControlName);
} else {
console.warn('Missing FormControlName directive from host element of the component');
}
} else {
console.warn('Can\'t find parent FormGroup directive');
}
}
}
I'm injecting the parent FormGroup
to the component and then getting the specific FormControl
from it using control name obtained through formControlName
binding.
However, be advised, that this solution is tailored specifically for the use case where FormControlName
directive is used on host element. It won't work in other cases. For this you will need to add some additional logic. If you think, that this should be addressed by Angular, make sure to visit the discussion.
Using formControlName
as an input parameter doesn't work when binding via the [formControl]
directive.
Here is a solution that works both ways without any input parameters.
export class MyComponent implements AfterViewInit {
private control: FormControl;
constructor(
private injector: Injector,
) { }
// The form control is only set after initialization
ngAfterViewInit(): void {
const ngControl: NgControl = this.injector.get(NgControl, null);
if (ngControl) {
this.control = ngControl.control as FormControl;
} else {
// Component is missing form control binding
}
}
}
Building upon previous answers and documentation found in a comment, here is what my opinion is the cleanest solution for a ControlValueAccessor
based component.
// No FormControl is passed as input to MyComponent
<my-component formControlName="myField"></my-component>
export class MyComponent implements AfterViewInit, ControlValueAccessor {
constructor(@Optional() @Self() public ngControl: NgControl) {
if (ngControl != null) {
// Setting the value accessor directly (instead of using
// the providers) to avoid running into a circular import.
ngControl.valueAccessor = this;
}
}
ngAfterContentInit(): void {
const control = this.ngControl && this.ngControl.control;
if (control) {
// FormControl should be available here
}
}
}
Please be aware that with this solution you won't need to specify the NG_VALUE_ACCESSOR provider on the component as it will result on a CI Circular Dependency. The constructor will set correctly the valueAccessor.
As @Ritesh has already written in the comment you can pass form control as an input binding:
<my-custom-form-component [control]="myForm.get('myField')" formControlName="myField">
</my-custom-form-component>
And then you can get form control instance inside your custom form component like this:
@Input() control: FormControl;
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