'How can I cast an AbstractControl to a FormControl in a template?

I have a FormGroup that I'm passing from a parent component to a child, and then within that FormGroup there's a control that I'm passing from the first child to a second child. However I then want to use the control in a button so that I can alter the state of the formgroup using it.

However when I do form.get('controlName'), it returns an AbstractControl, and when I try to pass the AbstractControl to the [formControl] directive of the button I get an error saying that Type AbstractControl is not assignable to abstract control.

Usually I'd just cast the control to a FormControl in my component but this won't work as I'm using the control directly in the template.

The form

this.form = this._formBuilder.group({
      cost: this._formBuilder.control(''),
      width: this._formBuilder.control('')
    })

First child template

<app-form-subsection-cost [subsectionFormControl]="form.get('cost')"></app-form-subsection-cost>

Second child

export class FormSubsectionCostComponent implements OnInit {

  @Input() subsectionFormControl: AbstractControl;

  constructor() { }

  ngOnInit(): void {
  }

}

Second child template

<button [formControl]="subsectionFormControl as FormControl" value="Yes">Yes</button> //Error on formControl directive

I can't do this:

<app-form-subsection-cost [subsectionFormControl]="form.get('cost')"
[subsectionFormControl]="form.get('cost') as FormControl"></app-form-subsection-cost>


Solution 1:[1]

What I do is define a method in my component to return the FormControl with a given name.

  fc(name) { return this.form.get(name) as FormControl; }

Then use it in the template:

  [formControl]="fc('subsectionFormControl')

Solution 2:[2]

You can write a method like Thor suggested, but unfortunately the best supported out-of-the-box solution I have found is to use the $any() cast function. This is less safe, but you don't have to add a cast function to every affected component.

Solution 3:[3]

For what it's worth: define setter / getter, and allow superclass on setter:

@Input() 
set control(control: AbstractControl) {
    this._control = control as FormControl;
}
get control(): FormControl {
    return this._control;
}

If there's a chance the cast in the setter will backfire, do a typeof + throw Error beforehand to fail-fast.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Thor
Solution 2 Coderer
Solution 3 Simon