Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

refactor(input): remove $$parserName from email, url #14292

Closed
wants to merge 1 commit into from

Conversation

Narretz
Copy link
Contributor

@Narretz Narretz commented Mar 21, 2016

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

What is the current behavior? (You can also link to an open issue here)

What is the new behavior (if this is a feature change)?

Does this PR introduce a breaking change?

Please check if the PR fulfills these requirements

Other information:

Since email and url don't use $parsers, we do not need to set the parserName.
This also reduces the number of times the valid / invalid classes are
set within for the input or possible parent forms.

This was discovered when fixing #14249. The additional class settings triggered
a cancellation of the structural animation. A general fix for this behavior has
landed as

Since email and url don't use $parsers, we do not need to set the parserName.
This also reduces the number of times the valid / invalid classes are
set within  for the input or possible parent forms.

This was discovered when fixing angular#14249. The additional class settings triggered
a cancellation of the structural animation. A general fix for this behavior has
landed as <insert commit here>
@gkalpak
Copy link
Member

gkalpak commented Mar 22, 2016

For one, I believe this is a (tiny) Breaking Change, because previously we had ng-valid/invalid-url (or -email), while now we have ng-valid/invalid-parse.

If the problem is caused by trying to add/remove the same class multiple times (or trying to add and remove the same class) - once because of a validator and once because of the parser (which happens to have the same name as the validator), then we are not really solving it.

That same problem could happen, if the user chose to add a validator with the same name as $$parserName for example. (This affects all input types, not just email/url.)

I believe we could instead fix this inside processParseErrors(), by changing it to:

         if (!parserValid) {
           forEach(ctrl.$validators, function(v, name) {
-            setValidity(name, null);
+            if (name !== errorKey) setValidity(name, null);
           });
           forEach(ctrl.$asyncValidators, function(v, name) {
-            setValidity(name, null);
+            if (name !== errorKey) setValidity(name, null);
           });
         }
         // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
         setValidity(errorKey, parserValid);

or better yet (to avoid the same error when $validators and $asyncValidators have the same key):

         if (!parserValid) {
+          Object.keys(ctrl.$validators || {}).
+            concat(Object.keys(ctrl.$asyncValidators || {})).
+            filter(function (key, idx, arr) { return (key !== errorKey) && (arr.indexOf(key) === idx); }).
+            forEach(function (key) { setValidity(key, null); }); 
-          forEach(ctrl.$validators, function(v, name) {
-            setValidity(name, null);
-          });
-          forEach(ctrl.$asyncValidators, function(v, name) {
-            setValidity(name, null);
-          });
         }
         // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
         setValidity(errorKey, parserValid);

@Narretz
Copy link
Contributor Author

Narretz commented Mar 22, 2016

With this change, we have now an additional ng-parse-valid class added to the element, but ng-(in)valid-email is on the element before and after the change.

I know that this doesn't fix all the cases where this can happen, I was just confused why url and email define the $$parserName at all. Maybe because they had a badInputChecker at some time (which uses a parser).

@gkalpak
Copy link
Member

gkalpak commented Mar 22, 2016

It's not only for our parsers; the same name will be set if the user adds their parsers to the input.
So, we might be breaking someone's code. (Note that if a parser returns undefined, then there will be no validation, thus no ng-invalid-email/url class (only ng-invalid-parse), which could mess up someone's styles.)

Just to be clear, there are two things I am saying here:

  1. Removing the $$parserName makes sense (because if nothing else it is confusing to have the parser be named the same as a validator), but it is a breaking change.
    This means that (a) it should be documented as such and (b) it should not be backported to 1.5.x
  2. Independently from (1), we should fix the underlying problem (which if I understand correctly happens when adding/removing the same class multiple times) as described in refactor(input): remove $$parserName from email, url #14292 (comment).
    This is not a breaking change and can/should be backported to 1.5.x

WDYT @Narretz ?

@Narretz
Copy link
Contributor Author

Narretz commented Mar 22, 2016

I forgot about other parsers, yes that's right. I'm in favor of fixing this "right" with a BC in 1.6. I'd actually like to get rid of $$parserName and the while undefined === invalid thing, too, but I don't know if it's too big of a change at this point in 1.x

@Narretz Narretz modified the milestones: 1.6.x, 1.5.x Mar 22, 2016
@petebacondarwin petebacondarwin modified the milestones: 1.6.x, 1.7.0 Oct 2, 2017
Narretz added a commit to Narretz/angular.js that referenced this pull request Nov 27, 2017
This commit changes how input elements use the private $$parserName
property on the ngModelController to name parse errors. Until now,
the input types (number, date etc.) would set $$parserName when
the inputs were initialized, which meant that any other parsers on
the ngModelController would also be named after that type. The
effect of that was that the `$error` property and the `ng-invalid-...`
class would always be that of the built-in parser, even if the custom
parser had nothing to do with it.

The new behavior is that the $$parserName is only set if the actual
parser is invalid, i.e. returns `undefined`.

Also, $$parserName has been removed from input[email] and input[url],
as these types do not have a built-in parser anymore.

BREAKING CHANGE:

Custom parsers that fail to parse on input types email, url, date, month, time,
datetime-local, week, do no longer set $error[inputType], and no longer set the class
`ng-invalid-[inputType]`. Instead, they set $error.parse and `ng-invalid-parse`.

Closes angular#14292
Closes angular#10076
Narretz added a commit to Narretz/angular.js that referenced this pull request Dec 1, 2017
This commit changes how input elements use the private $$parserName
property on the ngModelController to name parse errors. Until now,
the input types (number, date etc.) would set $$parserName when
the inputs were initialized, which meant that any other parsers on
the ngModelController would also be named after that type. The
effect of that was that the `$error` property and the `ng-invalid-...`
class would always be that of the built-in parser, even if the custom
parser had nothing to do with it.

The new behavior is that the $$parserName is only set if the built-in
parser is invalid i.e. returns `undefined`.

Also, $$parserName has been removed from input[email] and input[url],
as these types do not have a built-in parser anymore.

BREAKING CHANGE:

*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month",
"time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and
the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no
longer set `ngModelController.$error.number` and the `ng-invalid-number` class.

Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and
`ng-invalid-parse`.

Closes angular#14292
Closes angular#10076
Narretz added a commit that referenced this pull request Dec 4, 2017
This commit changes how input elements use the private $$parserName
property on the ngModelController to name parse errors. Until now,
the input types (number, date etc.) would set $$parserName when
the inputs were initialized, which meant that any other parsers on
the ngModelController would also be named after that type. The
effect of that was that the `$error` property and the `ng-invalid-...`
class would always be that of the built-in parser, even if the custom
parser had nothing to do with it.

The new behavior is that the $$parserName is only set if the built-in
parser is invalid i.e. returns `undefined`.

Also, $$parserName has been removed from input[email] and input[url],
as these types do not have a built-in parser anymore.

Closes #14292
Closes #10076
Closes #16347

BREAKING CHANGE:

*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month",
"time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and
the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no
longer set `ngModelController.$error.number` and the `ng-invalid-number` class.

Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and
`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers
and custom parsers easier.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants