Our content is free thanks to ag-Grid

ag-Grid is the industry leading JavaScript datagrid

ag-grid.com

Angular Forms Story: Strong Types

Post Editor

How I strongly typed my Angular forms. And you can too using my small library!

5 min read
post

Angular Forms Story: Strong Types

How I strongly typed my Angular forms. And you can too using my small library!

post
post
5 min read
5 min read

Angular Reactive Forms are not strongly typed! AbstractControl and it’s implementations FormControl, FormGroup and FormArray do not support strong typing their value or changes or any other property/method. For the longest time, I accepted and worked around that, considering it just one of life’s things , but then I decided to change that.

In this article I want to share the process I went through while gradually adding strong types to my forms. If you just want to see the end result, check out ngx-forms-typed library. It’s strongly typed, backward compatible (works with Angular 2.4.0) and reduces boilerplate.


For my job, I had to do a reasonably large form, enough to make me fear the business logic mixing with the form logic and sub-parts of the form. I wanted to have a strongly-typed, easy to use and @angular/forms way to extract sub-forms components from a large form. Much like we extract components to handle their own concerns. The (sub) form component would need to conform to the following requirements:

  • have a strongly typed model
  • @angular/forms — compatible
  • use existing abstraction for communication, like AbstractControl and ControlValueAccessor, to handle validation, status changes, etc.

In short, I wanted to have a component that could be used as a single control in a larger form and be a form in its own right with multiple fields, validation, status changes. And all the while keeping the parent form happily unaware of implementation details and communicating via the AbstractControl API.
For example Address as a sub-form in a Person form and in an Order form. But the full form in a NewAddress form.

This work draws some inspiration and ideas from “Working with Angular forms in an enterprise environment” and “Building scalable robust and type safe forms with Angular” . I would encourage you to read those articles.

In the beginning

I was decorating my FormGroup-s and their constituent FormControl-s with types, like this:

Now I could rely on strong typing during refactoring or adding/removing features. Any Person type change would trickle down to the form — for example, if the property address was to be added I would get an error immediately (vs run time and maybe):

Screenshot describing an error of type: “ Property ‘address’ is missing in type ‘{ name: FormControl; email: FormControl; }’

Then I suffered the form.controls.get('name') way of reaching my controls. I did not like the pattern of creating a public getter for it. Mainly, I wanted type safety. So:

Yaay, intellisense! Productivity!

Now I had intellisense for my controls that significantly increased my productivity. And with a bit more finesse I had a type-dependent form group (or model-dependent if you will). See this little change trickling down:

Notice the email changed to emailS and causing Typescript to give me a very helpful error message :)

Did you notice the as unknown as PersonFormGroup (at the end of the form group instantiation form = new Form(…) as unknown …)— that’s what I was referring to as beating Typescript. In this case, I knew better than it what the actual shape of the run-time thing is! This is the only case so far usually, Typescript ️knows better!

Taking this work to the next step is to type as much of the FormControl/FormGroup/FormArray types as possible. And the result is the package ngx-forms-typed. It provides the TypedFormControl (GitHub src) TypedFormGroupand TypedFormArray types.

For example, let’s take a look at TypedFormControl<K>:

It adds a strong type to the value and valueChanges properties of FormControl so that you know what shape the value is when using it. It also strong types the method setValue so that you’d need to pass in a value of the expected type and maybe options — also strong typed. reset method is strong typed too. See ResetValue<K> source.

And helper functions for creating instances with those types typedFormControl (GitHub src), typedFormArray and typedFormGroup which make the creation of forms strongly typed too:

Typescript error pushing us towards the required properties and their types! 
Does intellisense too!

The function itself only instantiates a FormGroup which is to say that it is compatible with existing forms code and can be used next to it without breaking it:

Types omitted for brevity. See full source here.

Nested forms

I wanted to enable forms and sub-forms to communicate i.e. when sub-form get’s touched — touch the parent when it gets invalid — make the parent invalid too. These two are supported by the ControlValueAccessor abstraction. I also wanted to force the sub-form to get touched on cue from the parent, in order to show validation, which is not supported by ControlValueAccessor. I wanted to use existing ControlValueAccessor-AbstractControl channels of communication.

I came up with a builder-like pattern for interaction with controls of a form group/array. It comes in a function called forEachControlIn (see full code in GitHub). Its goal is to make interacting with controls in a form and between forms easy. It relies on having references to the forms.

Finally, to avoid boilerplate I tied it all in a ControlValueAccessorConnector (see full code on GitHub). It handles all the connection logic between a parent form and a sub-form.

See a live example here and code in Stackblitz.

Just like a user would expect, pressing the Submit button shows validation for the whole page. Blurring an input control shows validation only for that control.

There's example of nested form using formGroup directive in the party-form.component and a stand-alone one, using ngModel in app.component.


This was a short introduction of the library. Would you like to see a deep dive? Vote here. I'm in the process of writing an article where I'll summarize my research in the @angular/forms space of packages, PRs, and articles.

There are other things I'm working on related to Angular forms and a few open source Angular Dev Tooling projects you can check out:

Thank you for reading!

Discuss with community

Share

About the author

author_image
Georgi Parlakov

Author of Angular libs like SCuri (Angular unit test automation!) and ngx-forms-typed (type your Angular forms!) that make developer's work easier. https://gparlakov.github.io/

author_image

About the author

Georgi Parlakov

Author of Angular libs like SCuri (Angular unit test automation!) and ngx-forms-typed (type your Angular forms!) that make developer's work easier. https://gparlakov.github.io/

About the author

author_image
Georgi Parlakov

Author of Angular libs like SCuri (Angular unit test automation!) and ngx-forms-typed (type your Angular forms!) that make developer's work easier. https://gparlakov.github.io/

NxAngularCli
NxAngularCli
NxAngularCli

Featured articles

RxJSpost
21 January 20214 min read
RxJS in Angular: Part III

In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

RxJSpost
21 January 20214 min read
RxJS in Angular: Part III

In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

Read more
RxJSpostRxJS in Angular: Part III

21 January 2021

4 min read

In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

Read more
Angularpost
20 January 20216 min read
Angular and SOLID principles

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

Angularpost
20 January 20216 min read
Angular and SOLID principles

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

Read more
AngularpostAngular and SOLID principles

20 January 2021

6 min read

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

Read more
Angularpost
14 January 20216 min read
Demystifying Taiga UI root component: portals pattern in Angular

Just before new year we announced our new Angular UI kit library Taiga UI. If you go through Getting started steps, you will see that you need to wrap your app with the tui-root component. Let's see what it does and explore what portals are and how and why we use them.