ControlValueAccessors popped up to me over the last months and I'm a bit confused on why or when I should use them instead of using @Input
with my reactive forms.
Here's a sample code for how I've worked with reactive forms so far:
@Component({
selector: 'app-my-component',
template: `<input [formControl]="form.get('specificControlName')" />`
// or the alternative method of [formGroup]="form" and formControlName="specificControlName"
})
export class MyComponent {
@Input() form: FormGroup; // passed in formGroup
ngOnInit() {
form.valueChanges.pipe(
// do rxjs magic here
).subscribe(value => {
// do something with the value
})
}
}
Reactive Forms save the state of the form, so I can access that state even from the parent components. I can also access all the different NgControl
attributes like valid
, disabled
, dirty
, and touched
.
What do ControlValueAccessors provide that this way of working with reactive forms don't? And what are some use cases where ControlValueAccessors work better than @Input
and @Output
in general?
EDIT:
https://medium.com/angular-in-depth/angular-nested-reactive-forms-using-cvas-b394ba2e5d0d
In this article the author mentions the following as the main differences:
Three Ways to implement nested forms:
...
- By passing a handle of the FormGroup to child components via Input and referencing it in child templates. There are couple of good tutorials on it.
But the con of using this approach is that you are tightly binding the parent form group with that of child group.
- Using Composite CVAs.
Pros: Highly Reusable, Portable. Better Encapsulation(Internal Form Controls of the component doesn’t necessarily need to be visible to parent components). This is best used when you have more number of form modules which is typically a large project.
Cons: Need to implement CVA interface results in boilerplate code.
This is interesting, but it raises a couple of more questions: Why and when do you not want your internal Form Controls to be visible to the parent? What does portable mean?
Also:
import { Component, OnInit } from '@angular/core';
import { ControlValueAccessor,NG_VALUE_ACCESSOR, NG_VALIDATORS, FormGroup,FormControl, Validator, Validators,AbstractControl, ValidationErrors } from "@angular/forms";
@Component({
selector: 'app-address-info',
templateUrl: './address-info.component.html',
styleUrls: ['./address-info.component.css']
})
export class AddressInfoComponent implements OnInit, ControlValueAccessor {
public addressForm: FormGroup = new FormGroup({
addressLine: new FormControl("",[Validators.required]),
areacode: new FormControl('', [Validators.required, Validators.maxLength(5)])
});
constructor() { }
ngOnInit() {
}
public onTouched: () => void = () => {};
writeValue(val: any): void {
val && this.addressForm.setValue(val, { emitEvent: false });
}
registerOnChange(fn: any): void {
console.log("on change");
this.addressForm.valueChanges.subscribe(fn);
}
registerOnTouched(fn: any): void {
console.log("on blur");
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
isDisabled ? this.addressForm.disable() : this.addressForm.enable();
}
}
when you're passing in a FormGroup
into a ControlValueAccessor
Component the author is initializing a new FormGroup
inside of the component with the same structure as the passed in object. Isn't it better then to just pass in the FormGroup
itself then? Or what benefits does the encapsulation provide?
EDIT2: This is an interesting video on the subject:
https://www.youtube.com/watch?v=CD_t3m2WMM8
ControlValueAccessorlink. Defines an interface that acts as a bridge between the Angular forms API and a native element in the DOM.
In Angular, form controls are classes that can hold both the data values and the validation information of any form element. Every form input you have in a reactive form should be bound by a form control. These are the basic units that make up reactive forms.
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
NG_VALUE_ACCESSOR provider specifies a class that implements ControlValueAccessor interface and is used by Angular to setup synchronization with formControl . It's usually the class of the component or directive that registers the provider.
Use Standard Reactive Form API whenever you are using native HTML elements (input, select, button, form, etc)
Use ControlValueAccessor for example when you have to work with custom HTML elements (i.e. mat-list, mat-table, ag-grid-table, etc), the reason is because it's an interface that acts as a bridge between Angular forms API and DOM elements. ControlValueAccessor example
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