Home > OS >  How to determine the type of a FormControl when using typed Reactive Forms in Angular?
How to determine the type of a FormControl when using typed Reactive Forms in Angular?

Time:08-13

Consider the following simple typed form:

const dateControl = new FormControl<number | null>(null);
const monthControl = new FormControl<MonthOption | null>(null);
const yearControl = new FormControl<number | null>(null);

const form = new FormGroup({
  ['date']: this.dateControl,
  ['month']: this.monthControl,
  ['year']: this.yearControl,
});

The type of this.dateControl is FormControl<number | null>, as you'd expect.

If I wanted to, separately, retrieve the control, so I can work with its value, and therefore I need to be sure of the type of the FormControl and therefore the type of the control value, I might want to use:

const control = form.get('date');

The type of control is AbstractControl<any, any> | null, again as you might expect as it is grabbing the control via a string.

When working with dynamically generated forms, controls may be added to the form using addControl which takes control: any.

Q: Is there any way of determining what the actual type of a FormControl is without retaining a reference to a FormControl it outside of the FormGroup?

EDIT:

If I construct the form as follows, the type is correct ( AbstractControl<number | null, number | null> | null):

const form = new FormGroup({
  date: this.dateControl,
  month: this.monthControl,
  year: this.yearControl,
});
    
const control = form.get('date');

CodePudding user response:

I tried to reproduce your code, but it seems I can correctly infer the type.

Inside a dummy component class:

form = new FormGroup<{
    date: FormControl<string|null>,
    dynamic?: FormControl<string|null>, // dynamic is optional
    name: FormControl<string|null>,
  }>({
    date: new FormControl('2022-01-21'),
    name: new FormControl('Francesco')
  });
  
  ngOnInit(): void {

    const val = this.form.get('date');

    this.form.addControl('dynamic', new FormControl(''))
    const dynamic = this.form.get('dynamic');
    
    console.log(val, typeof val?.value);          // Prints FormControl, 'string'
    console.log(dynamic, typeof dynamic?.value);  // Prints FormControl, 'string'

UPDATE If you want to use the addControl method, then you should define an optional FormControl in the FormGroup. However, this might not always be possible if you need to dynamically add many fields. From the docs of the addControl:

In a strongly-typed group, the control must be in the group's type (possibly as an optional key).


As a side note, unless you want to explicitly show the type, you can omit to define the primitive type for the FormControls (new FormControl<string | null>). Typescript will automatically infer the type <string | null> using the Reactive Form API. All types are made nullable by default.

If you do not want nullable values, you can:

use the nonNullable property of the FormBuilder class:

constructor(private fb: FormBuilder) {}

// Sets all controls to be non-nullable
  const registrationForm = this.fb.nonNullable.group({
    username: '',
    email: ''
  });

  // Sets only the "username" control to be non-nullable
  const registrationForm2 = this.fb.group({
    username: this.fb.nonNullable.control(''),
    email: ''
  });

use the NonNullableFormBuilder class (this will make all controls nonNullable)

constructor(private nnfb: NonNullableFormBuilder) {}

ngOnInit() {
  const registrationForm = this.nnfb.group({
    username: '',
    email: ''
});
}
  • Related