Angular strongly typed reactive forms

10,435

Solution 1

The most elegant solution is leveraging TypeScript declaration files (*.d.ts) to introduce generic interfaces extending the standard form classes like AbstractControl, FormControl, etc. It doesn’t introduce any new functionality and has no footprint in the compiled JavaScript, but at the same time enforcing strong type checking.

It was suggested by Daniele Morosinotto in March this year and there are talks now to include it in Angular 9.

Adopting the solution is straightforward:

  1. Download TypedForms.d.ts from this gist and save it as src/typings.d.ts in your project (Angular 6+ already knows how to use this file).
  2. Start using the new types (FormGroupTyped<T>, FormControlTyped<T>, etc.) whenever you need a strong type validation (see examples in that gist or stackblitz).

For more information, check out a blog post analysing available solutions for strongly typed forms.

Solution 2

For ones who want another solution. I found this article talking about strong type for angular form. Below is my sumary.

interface Person {
  name: string;
  email: string
}

// Controls in a form group that would emit a Person as it's value
type PersonControls = { [key in keyof Person]: AbstractControl };
type PersonFormGroup = FormGroup & { value: Person, controls: PersonControls };

export class MyFormComponent {
  form = new FormGroup({
    name: new FormControl(),
    email: new FormControl()
  } as PersonControls) as PersonFormGroup;

  init() {
    const name = this.form.controls.name; // strong typed!
  }
}

Solution 3

I had a similar issue and this was my solution. I really only cared about the type of the 'value' of the form not the form itself. It ended up looking something like this.

export interface UserFormValue {
  first_name: string
  last_name: string
  referral: string
  email: string
  password: string
}
...

ngOnInit() {
  this.userForm = this.fb.group({
    first_name: [ '', Validators.required ],
    last_name: [ '', Validators.required ],
    referral: [ '' ],
    email: [ '', [ Validators.required, Validators.email ] ],
    password: [ '', [ Validators.required, Validators.minLength(8) ] ],
  });
}

...

Then in the template submit the value

<form [formGroup]="userForm" (ngSubmit)="onSubmit(userForm.value)">
   ...
</form>

Now you can add a type to the submit function

onSubmit(userForm: UserFormValue) {
   ...
}

It's not perfect but has been good enough for my use cases. I really wish there was like this.

userForm: FormGroup<UserFormValue>

Solution 4

The solution I ended up using was a library I found called ngx-strongly-typed-forms

It enables you to have strongly typed FormControls, FormGroups, and FormArrays. There are some limitations but it's definitely helped a lot in my project.

You can see the documentation at https://github.com/no0x9d/ngx-strongly-typed-forms

Solution 5

if you have a nested type of groups then you can do sometihng like this:
**models.ts

export type TCreateUserFields = {
    first_name: string,
    last_name: string,
    accept_terms: boolean,
};
export type TPasswordsControls = {
    passwords: FormGroup & {
        password: AbstractControl,
        confirm_password: AbstractControl
    }
}
export type TPasswordsFields = {
    passwords: {
        password: string,
        confirm_password: string
    }
}
export type TAllFields = TCreateUserFields & TPasswordsFields;
export type TAllControls = TCreateUserControls & TPasswordsControls;
export type TCreateUserControls = {
    [key in keyof TCreateUserFields]: AbstractControl
};
export type TCreateUserFormGroup = FormGroup & {value: TAllFields, controls: TAllControls};

**component.ts
this.registerationForm = this.fb.group(
{
    first_name: new FormControl("", [Validators.required]),
    last_name: new FormControl("", [Validators.required]),
    accept_terms: new FormControl(false, [Validators.required]),
    passwords: new FormGroup(
        {
            password: new FormControl("", [Validators.required, Validators.pattern(/^[~`!@#$%^&*()_+=[\]\{}|;':",.\/<>?a-zA-Z0-9-]+$/)]),
            confirm_password: new FormControl("", [Validators.required, Validators.pattern(/^[~`!@#$%^&*()_+=[\]\{}|;':",.\/<>?a-zA-Z0-9-]+$/)]),
        }, { 
            validators: <ValidatorFn>pwdConfirming({key:'password', confirmationKey:'confirm_password'})
        }
    )
} as TCreateUserControls) as TCreateUserFormGroup;
Share:
10,435

Related videos on Youtube

Tom Vinnicombe
Author by

Tom Vinnicombe

Updated on June 04, 2022

Comments

  • Tom Vinnicombe
    Tom Vinnicombe almost 2 years

    I'm looking to refactor a large set of components in my Angular project to have strongly typed FormGroups, FormArrays, and FormControls.

    I'm just looking for a good way to implement strongly typed reactive forms. Could anyone provide suggestions/recommendations from their own experiences?

    Thank you.

    EDIT:

    To clarify, by strongly typed I mean currently when I create a FormGroup or FormArray I have no way to specify the structure of the actual form inside it. When I pass this form around to various components in my app, I then feel I am making it more difficult to maintain.

    • Tom Vinnicombe
      Tom Vinnicombe about 5 years
      @SiddAmjera To clarify, by strongly typed I mean currently when I create a FormGroup or FormArray I have no way to specify the structure of the actual form inside it. When I pass this form around to various components in my app, I then feel I am making it more difficult to maintain.
    • Akxe
      Akxe over 2 years
      Angular team is currently working on it, they have MVP of generic/typed form! github.com/angular/angular/issues/13721#issuecomment-9429108‌​23
    • Tom Vinnicombe
      Tom Vinnicombe
      @SiddAmjera Sorry maybe I'm not explaining it clearly enough. It's not a specific piece of code. But hopefully this explains the issue: stackblitz.com/edit/angular-tlfrgt
  • Alfa Bravo
    Alfa Bravo over 4 years
    From where do you import FormGroupTyped?
  • Alfa Bravo
    Alfa Bravo over 4 years
    Nevermind, I was using a Lib and you have to copy the TypedForms.d.ts file into each library as well...
  • Kisinga
    Kisinga about 4 years
    I created an issue over a month ago and this doesn't seem to have gotten ANY response yet. From the looks of it, this project may have gone stale and I don't recommend anyone to use it!
  • hlovdal
    hlovdal about 3 years
    The (still unsolved) angular issue for missing type information is github.com/angular/angular/issues/13721. There are some stronger alternatives mentioned there (like @ng-stack/forms).