question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Canonical: Automatically generate Forms control types from model type

See original GitHub issue

Which @angular/* package(s) are relevant/related to the feature request?

forms

Description

Abstract In the following, I will propose a helper to pass API-like interfaces to strictly typed FormGroup in order to not have to rewrite types. Also, this propose could also fix a bug affecting FormBuilder which returns the wrong type when dealing with objects

Details When building a strictly-typed FormGroup, it expects an interface having already the right types defined for its properties, i.e.:

interface SomeInterface {
  foo: string;
  bar: {
    baz: number;
  }
}

interface SomeInterfaceForm {
  foo: FormControl<string>;
  bar: FormGroup<{
    baz: FormControl<number>;
  }>;
}

However, this is a limitation when dealing with API, because it forces to duplicate interfaces in order to define the right types for FormGroup. Also, this may be the source of possible bugs because changes in API’s data should be reflected also in form interface.

Moreover, if using FormBuilder, the resulting type is not correctly inferred:

const fg = formBuilder.group<SomeInterface>({
  foo: '',
  bar: {
    baz: 0
  }
});

fg.controls.bar.controls // Error: Property 'controls' does not exist on type 'FormControl<{ baz: number; }>'

Proposed solution

I propose to create and expose a helper type able to infer the correct type given its input, something like the following:

type Controls<T> = { [k in keyof T]: Form<T[k]> }
type Form<T> = [T] extends [boolean | number | string | null | undefined] ? FormControl<T> :
               [T] extends [(infer U)[]] ? FormArray<Form<U>> :
               FormGroup<Controls<T>>

and apply it to FormGroup like:

class FormGroup<T> {
  constructor(public controls: Controls<T>) {
    // ...
  }
}

In this way, one can define a FormGroup starting from API interface in this way:

const fg = new FormGroup<SomeInterface>({
  foo: new FormControl<string>('')
  // ...
}):

having back SomeInterface when accessing fg.value (related to #46073)

Alternatives considered

None

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:43
  • Comments:10 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
dylhunncommented, Jul 18, 2022

It would be really nice if Forms could automatically infer the model types from your data types. The reason we didn’t include this in v14 is that, unfortunately, it doesn’t work in the general case. Here’s why:

Consider you have a data model that represents a date:

interface Date {
  day: number;
  month: string;
  year: number;
}

This could correspond to a form with three fields:

{
  day: FormControl<number>;
  month: FormControl<string>;
  year: FormControl<number>;
}

Or it could correspond to a datepicker widget, which is a single custom control which returns an object:

FormControl<{
  day: number;
  month: string;
  year: number;
}>

And it’s impossible to know which generally!

It would be nice to make improvements in this area, but this general issue would need to be designed around.

0reactions
ricardojbertolincommented, Nov 24, 2022

Let me show you a solution by using Typescript utilities that might be helpful in some situations.

In case you can’t initialize your FormGroup in the constructor, becase you need some data coming from @Input (for example), there is a way that Typescript Utilities can help.

Imagine the following scenario:

@Input() myStringData: string;
@Input() myBoolData: boolean;
formGroup: FormGroup;


ngOnInit() {

  this.formGroup= new FormGroup({
	  myString: new FormControl(this.myString),
	  myBoolData: new FormControl(this.myBoolData),
  })

}

The type inferral of the FormGroup doesn’t work because the initialization is not done in the declaration. Now, let’s do our magic.

First, encapsulate the FormGroup initialization into an isolated function, like this:

const initFormGroup = (myString: string; myBoolData: boolean) =>
	new FormGroup({
		myString: new FormControl(this.myString),
		myBoolData: new FormControl(this.myBoolData),
	});

You can check that the resulting FormGroup type is properly inferred.

And then, by using ReturnType utility (see https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype) we’ll indicate in the declaration which type the formgroup is:

formGroup: ReturnType<typeof initFormGroup>;

Now you can check that the “formGroup” property has the correct type inferred! This might be very useful for large FormGroups or when using Interfaces.

I hope can be useful for you too!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Canonical Forms - 2022.2 English - Xilinx
There are two main canonical forms for the dataflow optimization: The canonical f. ... Abstract Parallel Programming Model for HLS · Control and...
Read more >
What Is a Canonical Data Model? CDMs Explained
CDMs are a type of data model that aims to present data entities and relationships in the simplest possible form to integrate processes ......
Read more >
More capable form controls - web.dev
The Form-associated custom elements API lets custom elements act more like built-in form controls. These two features can be used to create new ......
Read more >
Creating a content entity type in Drupal 8 | Entity API
This page provides an example of how to create a content entity type, ... Calls the form.add controller, defined in the contact entity....
Read more >
URL Canonicalization and the Canonical Tag | Documentation
When a site has duplicate content, Google chooses the canonical URL. ... minor changes in sorting or filtering of list pages don't make...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found