Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(forms): don't mutate validators array #47830

Closed

Conversation

crisbeto
Copy link
Member

Fixes that the AbstractControl was mutating the validators arrays being passed into the constructor an helper methods like setValidators.

Fixes #47827.

@crisbeto crisbeto added action: review The PR is still awaiting reviews from at least one requested reviewer area: forms target: patch This PR is targeted for the next patch release labels Oct 21, 2022
@ngbot ngbot bot added this to the Backlog milestone Oct 21, 2022
@crisbeto crisbeto force-pushed the 47827/forms-validators-mutable branch from 7bce4ac to 88fd6ee Compare October 21, 2022 11:11
@@ -632,12 +632,6 @@
{
"name": "cleanUpView"
},
{
Copy link
Member Author

@crisbeto crisbeto Oct 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure why this was dropped since the functions are still used inside setValidators and setAsyncValidators. My guess is because the calls were removed from the constructor.

@crisbeto crisbeto marked this pull request as ready for review October 21, 2022 11:28
@crisbeto crisbeto added target: rc This PR is targeted for the next release-candidate and removed target: patch This PR is targeted for the next patch release labels Oct 21, 2022
@@ -372,15 +372,15 @@ export abstract class AbstractControl<TValue = any, TRawValue extends TValue = T
*
* @internal
*/
private _composedValidatorFn: ValidatorFn|null;
private _composedValidatorFn!: ValidatorFn|null;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could also initialize these to null, but it seemed redundant since they're guaranteed to be defined in the constructor, TS just isn't able to figure it out statically.

@@ -401,7 +401,7 @@ export abstract class AbstractControl<TValue = any, TRawValue extends TValue = T
*
* @internal
*/
private _rawAsyncValidators: AsyncValidatorFn|AsyncValidatorFn[]|null;
private _rawAsyncValidators!: AsyncValidatorFn|AsyncValidatorFn[]|null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be:

Suggested change
private _rawAsyncValidators!: AsyncValidatorFn|AsyncValidatorFn[]|null;
private _rawAsyncValidators: ReadonlyArray<AsyncValidatorFn> = [];

Always an array and always readonly

Copy link
Member Author

@crisbeto crisbeto Oct 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The array can be mutated later on by the addValidators and removeValidators methods. We just don't want to mutate the original array given to us by the author.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always an array and always readonly

There is a code path where _rawValidators and _rawAsyncValidators may become a function:

https://github.com/angular/angular/blob/main/packages/forms/src/model/abstract_model.ts#L441-L446

However, we can do further refactoring (in a followup PR if needed) to replace the implementation of the validator and asyncValidator setters with setValidators and setAsyncValidators calls, in which case the _rawValidators and _rawAsyncValidators would always be arrays.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this code path is relevant for the fix here though. We only need to clone the value when it's being set to an array and the validator getter/setter is a ValidatorFn|null.

Copy link
Contributor

@AndrewKushnir AndrewKushnir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

I've left a proposal on additional refactoring, but we can do that in a followup PR.

@@ -401,7 +401,7 @@ export abstract class AbstractControl<TValue = any, TRawValue extends TValue = T
*
* @internal
*/
private _rawAsyncValidators: AsyncValidatorFn|AsyncValidatorFn[]|null;
private _rawAsyncValidators!: AsyncValidatorFn|AsyncValidatorFn[]|null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always an array and always readonly

There is a code path where _rawValidators and _rawAsyncValidators may become a function:

https://github.com/angular/angular/blob/main/packages/forms/src/model/abstract_model.ts#L441-L446

However, we can do further refactoring (in a followup PR if needed) to replace the implementation of the validator and asyncValidator setters with setValidators and setAsyncValidators calls, in which case the _rawValidators and _rawAsyncValidators would always be arrays.

@crisbeto crisbeto added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Oct 24, 2022
@pkozlowski-opensource
Copy link
Member

This PR was merged into the repository by commit 0329c13.

pkozlowski-opensource pushed a commit that referenced this pull request Oct 24, 2022
Fixes that the `AbstractControl` was mutating the validators arrays being passed into the constructor an helper methods like `setValidators`.

Fixes #47827.

PR Close #47830
pkozlowski-opensource added a commit to pkozlowski-opensource/angular that referenced this pull request Oct 24, 2022
@crisbeto crisbeto reopened this Oct 24, 2022
@crisbeto crisbeto removed the action: merge The PR is ready for merge by the caretaker label Oct 24, 2022
@crisbeto crisbeto marked this pull request as draft October 24, 2022 18:24
pkozlowski-opensource added a commit that referenced this pull request Oct 25, 2022
pkozlowski-opensource added a commit that referenced this pull request Oct 25, 2022
control.setAsyncValidators(originalValidators);
control.addAsyncValidators(asyncValidator('two'));

expect(originalValidators.length).toBe(1);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that better way is to check the deep equality of the array before and after the manipulations.

nouraellm pushed a commit to nouraellm/angular that referenced this pull request Nov 6, 2022
Fixes that the `AbstractControl` was mutating the validators arrays being passed into the constructor an helper methods like `setValidators`.

Fixes angular#47827.

PR Close angular#47830
nouraellm pushed a commit to nouraellm/angular that referenced this pull request Nov 6, 2022
Fixes that the `AbstractControl` was mutating the validators arrays being passed into the constructor an helper methods like `setValidators`.

Fixes angular#47827.
@crisbeto crisbeto force-pushed the 47827/forms-validators-mutable branch from 88fd6ee to 93405c2 Compare November 16, 2022 08:59
@crisbeto crisbeto added target: patch This PR is targeted for the next patch release and removed target: rc This PR is targeted for the next release-candidate labels Nov 16, 2022
@crisbeto
Copy link
Member Author

I've fixed the issue that caused the PR to be reverted previously. I also have a passing TGP for it.

@crisbeto crisbeto marked this pull request as ready for review November 16, 2022 12:38
@crisbeto crisbeto added action: merge The PR is ready for merge by the caretaker merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note labels Nov 16, 2022
@crisbeto
Copy link
Member Author

Caretaker Note: Please ignore the google-internal-tests status. TGP status isn't being picked up

@dylhunn
Copy link
Contributor

dylhunn commented Nov 17, 2022

This PR was merged into the repository by commit 779a76f.

dylhunn pushed a commit that referenced this pull request Nov 17, 2022
Fixes that the `AbstractControl` was mutating the validators arrays being passed into the constructor an helper methods like `setValidators`.

Fixes #47827.

PR Close #47830
@dylhunn dylhunn closed this in 779a76f Nov 17, 2022
@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Dec 18, 2022
trekladyone pushed a commit to trekladyone/angular that referenced this pull request Feb 1, 2023
Fixes that the `AbstractControl` was mutating the validators arrays being passed into the constructor an helper methods like `setValidators`.

Fixes angular#47827.

PR Close angular#47830
trekladyone pushed a commit to trekladyone/angular that referenced this pull request Feb 1, 2023
Fixes that the `AbstractControl` was mutating the validators arrays being passed into the constructor an helper methods like `setValidators`.

Fixes angular#47827.

PR Close angular#47830
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: forms merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AbstractControl constructed with an array of validators mutates the original array on control.addValidators()
6 participants