I have a simple FormGroup in an Angular component:
// app.component.ts, part 1
export class AppComponent implements OnInit {
protected form = new FormGroup({
ctl: new FormControl(),
arr: new FormArray([]),
});
The FormGroup can be disabled via a checkbox:
// app.component.ts, part 1
enabledCtl = new FormControl(true);
ngOnInit() {
this.enabledCtl.valueChanges.subscribe((enabled) => {
if (enabled) {
this.form.enable();
} else {
this.form.disable();
}
}
The FormArray, arr, is not represented in the HTML. The FormControl, ctl is bound to a custom component, custom-control.
<!-- app.component.html, part 1 -->
<input type="checkbox" id="enable" [formControl]="enabledCtl">
<label for="enable">Enabled</label>
<div [formGroup]="form">
<custom-control formControlName="ctl"></custom-control>
</div>
The custom control implements ControlValueAccessor and is no more than a wrapper around another FormControl:
// custom-control.component.ts
@Component({
selector: 'custom-control',
imports: [ReactiveFormsModule],
template: '',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomControlComponent),
multi: true,
},
]
})
export class CustomControlComponent implements ControlValueAccessor {
protected ctl = new FormControl()
// All of the rest is boilerplate.
public writeValue(v: any): void {
this.ctl.setValue(v)
}
public registerOnChange(fn: (...args: unknown[]) => unknown): void {
this.ctl.valueChanges.subscribe(fn as any)
}
protected _onTouched = () => {}
public registerOnTouched(fn: (...args: unknown[]) => unknown): void {
const oldOnOnTouched = this._onTouched
this._onTouched = () => {
oldOnOnTouched()
fn()
}
}
public setDisabledState(isDisabled: boolean): void {
if (isDisabled) {
this.ctl.disable()
} else {
this.ctl.enable()
}
}
}
With this setup, when the user checks the checkbox bound to enabledCtl, the FormGroup form should get enabled. When they uncheck it, the form should get disabled.
However, when check the FormGroup's status, it never changes:
<!-- app.component.html, part 2 -->
Status: {{ form.disabled ? 'disabled' : 'enabled' }}
@if(form.enabled !== enabledCtl.value) {
<strong> but should be {{ enabledCtl.value ? 'enabled' : 'disabled' }}</strong>
}
When the checkbox is unchecked, the FormGroup’s disabled flag doesn’t get changed and I get the following output in the HTML:
Status: enabled but should be disabled
The FormGroup’s status updates according to my expectations if I make any single one of these changes:
- I replace the FormControl with another FormArray and remove the binding in the HTML
- I remove the FormArray from the FormGroup
- I bind the FormControl to a regular HTML input element, say
<input type="text" formControlName="ctl">
Only when I use a custom form control and have a FormArray as its sibling in the FormGroup does the FormGroup not update as I expect.
Why does the FormGroup’s disabled status not update with the above setup?