Reputation: 5876
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
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
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
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
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