6

How should I declare this type ?

Let say I've a FormGroup

  1. I instantize it in the constructor.
export interface MyType {
 myValue: string
}
myForm: FormGroup
myArray: FormArray // <FormControl<MyType>>

constructor(private _formBuilder: FormBuilder) {
  myForm = new FormGroup({
    myArray: this._formBuilder.array([])
  })
}
  1. Because I have somewhere a button that let me add a new element to the array, I do as follow
addElement() {
  this.myArray = this.myForm.get('myArray') as unknown as FormArray

  this.myArray.push(this.newElement())
}

private _newElement(): FormGroup { // What type to use ? 
  return this._formBuilder.group({
     myValue: ''
  })
}

But when I do use the

myArray: FormArray<FormControl<MyType>>

I get the following error

Argument of type 'FormGroup<any>' is not assignable to parameter of type 'FormControl<MyType>'.
  Type 'FormGroup<any>' is missing the following properties from type 'FormControl<MyType>': defaultValue, registerOnChange, registerOnDisabledChange

Somebody knows

  • which type I should here private _newElement(): FormGroup { // What type to use ?
    or
  • Is this correct myArray: FormArray<FormControl<MyType>>? -> FormGroup<FormControl<MyType>> being incorrect
1
  • 1
    Having the same issu,e not much documentation on it out there Commented Jul 8, 2023 at 8:18

2 Answers 2

6
  1. Declare the interface/class for the Typed Form.
export class MyTypeForm {
  myValue: FormControl<string>;
}
  1. Declare myArray type as FormArray<FormGroup<MyTypeForm>>.
myArray: FormArray<FormGroup<MyTypeForm>>;
  1. The _newElement method to return FormGroup<MyTypeForm> type.
private _newElement(): FormGroup<MyTypeForm> {
  return this._formBuilder.group<MyTypeForm>({
    myValue: this._formBuilder.control(''),
  });
}

Demo @ StackBlitz

Reference: Angular Strictly Typed Forms (Complete Guide)

Sign up to request clarification or add additional context in comments.

3 Comments

Thx, just a little question, but the MyType interface does already exists and I will like to use it directly instead of having to recreated a new class. Is this possible or is your answer the only way?
You can create generic interface that defines each property as FormControl like export type FormControlType<T> = {[key in keyof T]: FormControl<T[key]>}. But it's not always usable, i.e. if some property is object, it will define FormControl with object as value (and it's OK), but maybe you wanted nested FormGroup instead.
Hi @RaphaëlBalet, I think that it is needed to create another interface specified for the FormGroup, not possible to use MyType interface in FormGroup as the compiler will complain the error. I think Edmunds' comment is useful as what he mentioned.
2

You can also do it via inference without declaring extra types. For clarity, I'm using functions to instantiate a new form, but it could be done inline as well.

Your component:

export class MyComponent {
  form = this.newForm();

  constructor(
    private readonly fb: NonNullableFormBuilder,
  ) {}

The dateRanges field is a FormArray. The empty array passed to fb.array() is strongly-typed using a mapping function:

  newForm() {
    return this.fb.group({
      year: this.fb.control({ value: this.current?.year ?? 2022, disabled: true }, [Validators.required]),
      active: [this.current?.active, [Validators.required]],
      dateRanges: this.fb.array([].map(this.newDateRangesForm)),
    });
  }

The mapping function for each item in the FormArray:

  newDateRangesForm(range: DateRange) {
    return this.fb.group({
      startDate: [range.startDate, [Validators.required]],
      endDate: [range.endDate, [Validators.required]],
    });
  }

Which yields a strongly typed form value:

const formValue = this.form.getRawValue();

enter image description here

1 Comment

I won't have time to implement it, but it looks like a really good solution, can someboy test it out and tell me if this should be the accepted answer ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.