You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Wrong FormControl type inference when created with FormBuild through group() with Validators and an array as value #46442
Which @angular/* package(s) are the source of the bug?
forms
Is this a regression?
No
Description
When creating a FormGroup with the new ng14 FormBuilder, if you try to add a control with both array value and validator(s) the infered type for this control become wrong. It seems the validator(s) are considered as value so we get a bad union typing.
Type 'FormGroup<{ test: FormControl<number[] | ((control: AbstractControl<any, any>) => ValidationErrors)>; }>' is not assignable to type 'FormGroup<{ test: FormControl<number[]>; }>'.
Types of property 'controls' are incompatible.
Type '{ test: FormControl<number[] | ((control: AbstractControl<any, any>) => ValidationErrors)>; }' is not assignable to type '{ test: FormControl<number[]>; }'.(2322)
Please provide the environment you discovered this bug in (run ng version)
This is happening because the nested arrays confuse the type inference. The root cause is that the FormBuilder API is so polymorphic that it's very tricky to get the types to always infer correctly. Fixing this in the short term will require a bit more investigation, but in the long run we should upgrade FormBuilder to a better API.
As a workaround, try constructing your FormArray directly as the value for test (e.g. with new FormArray()).
@dylhunn thank you for the comment. For now on I will stop using short array synthax as it's confusing anyway.
And to be clear, in the example it's a FormControl that is used with array value, not a FormArray. So I guess you meant using new FormControl<string[]>([]) or fb.control<string[]>([]) do you ?
expects validators and asyncValidators for AbstractControlOptions when using AbstractControl should support the type of strong typed FormGroup.
why should this be mandatory to access formGroup controls by props not by get('name of control')
formOptions: AbstractControlOptions={validators: this.passEqualValidator};
passForm: FormGroup<IPasswordForm>=newFormBuilder().group({pass1: newFormControl(null,Validators.compose([Validators.required,Validators.minLength(8),Validators.maxLength(25),isAscii])),pass2: newFormControl(null,Validators.compose([Validators.required,Validators.minLength(8),Validators.maxLength(25),isAscii]))},this.formOptions);// getting control by string is very bad when doing some refactoringpassEqualValidator(form: AbstractControl<IPasswordForm>): ValidationErrors{consterr={notEq: true};letcondition=false;if(form.get('pass1').value&&form.get('pass2').value){condition=form.get('pass1').value!==form.get('pass2').value;}returncondition ? err : null;}
@sysmat Sorry to say that, but I think your comment is off topic and should be posted on Stackoverflow. Anyway I will try to help you.
You probably missunderstood the AbstractControl<T> generic type. Unlike FormGroup<T>, it represents the value schema (in your case { pass1: string | null, pass2: string | null }). Also instead of using .get() you should use .value as it's more relevant and simplier in your case. Also, you should use DI instead of instanciate FormBuilder yourself or use new FormGroup(...). In few words, here is your revised and errorless code
// I think it deserves to be outside you component and maybe in it own fileexportconstpassEqualValidator: ValidatorFn=(control: AbstractControl<{pass1: string|null,pass2: string|null}>)=>{returncontrol.value.pass1===control.value.pass2 ? null : {notEq: true};}// The code after this point should be inside your component ...// Declare the shape of your form
passForm: FormGroup<{pass1: FormControl<string|null>,// you are not setting controls as nonNullable so the value can be null (cf. doc)pass2: FormControl<string|null>,}>// use DI to inject FormBuilderconstructor(fb: FormBuilder){// you don't need `Validators.compose()`, an array of validators is enoughconstpassControlValidators=[Validators.required,Validators.minLength(8),Validators.maxLength(25),isAscii]this.passForm=fb.group({pass1: fb.control('',passControlValidators),pass2: fb.control('',passControlValidators)},{validators: passEqualValidator});}
After trying many ways of initializing forms since version 14, I think the form builder doesn't bring much value than instanciate directly form controls. Eventually the only benefit is the .nonNullable shortcut. This benefit could be replaced with an eventual new NonNullableFormControl or NonNullableFormArray class (in the case of FormArrays I think it would be better to declare them non nullable and initialize them with an empty array by default)
But what is the point of this comment ? IMO dropping FormBuilder feature whould give Angular team more time and resource to focus on other more complex and needed features
Of course it's just my opinion
Reacted by Emmanuel Roux, János Dobszai and Mike Drakoulelis
Hello regarding this topic i have a concern.
I'm trying to use NonNullableFormBuilder to create my FormGroup with nonNullable FormControl's but for some reason if i need to still put nonNullable: true into the FormControl. I link an stackblitz example, apreciate if someone could tell me if this is related to this bug or it's another bug which should be reported aswell.
Activity
dylhunn commentedon Jun 22, 2022
This is happening because the nested arrays confuse the type inference. The root cause is that the
FormBuilderAPI is so polymorphic that it's very tricky to get the types to always infer correctly. Fixing this in the short term will require a bit more investigation, but in the long run we should upgrade FormBuilder to a better API.As a workaround, try constructing your
FormArraydirectly as the value fortest(e.g. withnew FormArray()).Mikastark commentedon Jun 23, 2022
@dylhunn thank you for the comment. For now on I will stop using short array synthax as it's confusing anyway.
And to be clear, in the example it's a FormControl that is used with array value, not a FormArray. So I guess you meant using
new FormControl<string[]>([])orfb.control<string[]>([])do you ?dylhunn commentedon Jun 23, 2022
Yes, if your
FormControltakes an array value, usenew FormControl<string[]>([]).sysmat commentedon Jun 29, 2022
validatorsandasyncValidatorsfor AbstractControlOptions when using AbstractControl should support the type of strong typed FormGroup.Mikastark commentedon Jun 29, 2022
@sysmat Sorry to say that, but I think your comment is off topic and should be posted on Stackoverflow. Anyway I will try to help you.
You probably missunderstood the
AbstractControl<T>generic type. UnlikeFormGroup<T>, it represents thevalueschema (in your case{ pass1: string | null, pass2: string | null }). Also instead of using.get()you should use.valueas it's more relevant and simplier in your case. Also, you should use DI instead of instanciateFormBuilderyourself or usenew FormGroup(...). In few words, here is your revised and errorless codedestus90 commentedon Jul 1, 2022
I think this happens not only when you are using an array value for your
FormControl.For this case we are getting a bad typing too:
Mikastark commentedon Jul 1, 2022
After trying many ways of initializing forms since version 14, I think the form builder doesn't bring much value than instanciate directly form controls. Eventually the only benefit is the
.nonNullableshortcut. This benefit could be replaced with an eventual newNonNullableFormControlorNonNullableFormArrayclass (in the case ofFormArrays I think it would be better to declare them non nullable and initialize them with an empty array by default)But what is the point of this comment ? IMO dropping FormBuilder feature whould give Angular team more time and resource to focus on other more complex and needed features
Of course it's just my opinion
dylhunn commentedon Aug 4, 2022
Edit -- removing this response. Inner
ControlConfigarrays are disallowed, so maybe this can be fixed without a whole new abstraction.dylhunn commentedon Aug 4, 2022
@destus90 I actually cannot reproduce your specific example. Mind linking me to a StackBlitz?
destus90 commentedon Aug 5, 2022
@dylhunn
from another issue https://stackblitz.com/edit/angular-ivy-qtt7cf?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.module.ts,src%2Fapp%2Fapp.component.html
iErKy commentedon Aug 11, 2022
Hello regarding this topic i have a concern.
I'm trying to use NonNullableFormBuilder to create my FormGroup with nonNullable FormControl's but for some reason if i need to still put nonNullable: true into the FormControl. I link an stackblitz example, apreciate if someone could tell me if this is related to this bug or it's another bug which should be reported aswell.
https://stackblitz.com/edit/angular-ivy-uzcbsd?file=src/app/app.component.ts
Thanks in advance.
dylhunn commentedon Aug 18, 2022
@destus90 and @Mikastark: This should now be fixed and released in
14.2.0-rc.0. Does it solve the issue for both of you? Thanks!@iErKy Can you please open a new issue? That will help us keep our topics separate. Thanks!
destus90 commentedon Aug 30, 2022
@dylhunn
works fine, thanks!
Mikastark commentedon Aug 30, 2022
I don’t use RC version of angular. Waiting for final 14.2 version.
destus90 commentedon Aug 31, 2022
@Mikastark
14.2 was published some days ago...
angular-automatic-lock-bot commentedon Oct 1, 2022
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.