ssougnez
ssougnez

Reputation: 5876

FormBuilder with strongly typed form in ng14

I have the following form:

const enum Fields {
  FirstName = 'firstName',
  LastName = 'lastName'
}

interface FormType {
  [Fields.FirstName]: FormControl<string | null>;
  [Fields.LastName]: FormControl<string | null>;
}

public form!: FormGroup<FormType>;

this.form = new FormGroup({
  [Fields.FirstName]: new FormControl(null, { validators: Validators.required }),
  [Fields.LastName]: new FormControl(null, { validators: Validators.required }),
});

The usage of the enum is an habit I took to avoid mistyping.

When I do this.form.getRawValue().firstName, the type of firstName is correctly defined as string | null. However, I prefer to use the FormBuilder to build my forms but I can't find a way to translate the form above using FormBuilder. I tried this:

this.form = this._fb.group<FormType>({
  [Fields.FirstName]: ['', Validators.required],
  [Fields.LastName]: ['', Validators.required],
});

I could of course use a simple FormGroup without any type but it would defeat the purpose :p

However, this does not even work as the inferred type of string | Validators. I tried different things without success.

Is it possible to use the FormBuilder with the strongly typed forms?

Upvotes: 14

Views: 19306

Answers (4)

potato
potato

Reputation: 21

BTW, for non-nullable (required) forms use nonNullable from FormBuilder like so:

this.form = this.fb.group<FormType>({
  [Fields.FirstName]: this.fb.nonNullable.control<string>('your type here', Validators.required),
  [Fields.LastName]:  this.fb.nonNullable.control<string>('', Validators.required),
})

Upvotes: 0

fizzlebix
fizzlebix

Reputation: 86

@Chellappan small addition:
at least you can strictly give the types in here and not inferring from the provided value by introducing a type specifically.

FormBuilder example:

this.form = this.fb.group<FormType>({
  [Fields.FirstName]: this.fb.control<string|null>(null, Validators.required),
  [Fields.LastName]:  this.fb.control<string|null>(null, Validators.required),
});

So by using form methods like setValue shouldn't result in an error when providing strings even if it's initialized with null

See also: https://angular.io/guide/typed-forms#specifying-an-explicit-type

Upvotes: 4

Vladislav
Vladislav

Reputation: 89

I have a solution for this problem i'm wrote some types for that https://github.com/iamguid/ngx-mf .

For example you can do something like that:

We define some model:

enum ContactType {
    Email,
    Telephone,
}

interface IContactModel {
    type: ContactType;
    contact: string;
}

interface IUserModel {
    id: number;
    firstName: string;
    lastName: string;
    nickname: string;
    birthday: Date;
    contacts: IContactModel[];
}

Then we define some magic type like:

Type Form = FormModel<IUserModel, { contacts: ['group'] }>

Then we have type based on our model before form will be init:

FormGroup<{
    firstName: FormControl<string | null>;
    lastName: FormControl<string | null>;
    nickname: FormControl<string | null>;
    birthday: FormControl<Date | null>;
    contacts: FormArray<FormGroup<{
        type: FormControl<ContactType | null>;
        contact: FormControl<string | null>;
    }>>;
}>

Upvotes: 5

Chellappan வ
Chellappan வ

Reputation: 27323

For Formbuilder you can provide a raw value, a control, or a boxed value, and the type will be inferred automatically.

this.form = this._fb.group({
  [Fields.FirstName]: ['', Validators.required],
  [Fields.LastName]: ['', Validators.required],
});

Update

We cannot pass type of the model, As FormType expect group with formControl we need to specify formControl to each control.

this.form = this.fb.group<FormType>({
  [Fields.FirstName]: this.fb.control('', Validators.required),
  [Fields.LastName]:  this.fb.control('', Validators.required),
});


this.form.value //value: Partial<{firstName: string | null;lastName: string | null;}>

Upvotes: 8

Related Questions