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

fix(input): fix step validation for input[number]/input[range] #15264

Closed
wants to merge 5 commits into from

Conversation

gkalpak
Copy link
Member

@gkalpak gkalpak commented Oct 13, 2016

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

What is the current behavior? (You can also link to an open issue here)
step validation (for input[number]/input[range]) fails on some occasions due to limitations of Floating Point Arithmetic (as described in #15257) and does not fully account for a stepBase as descussed in 9a8b8aa#commitcomment-19108436.

What is the new behavior (if this is a feature change)?
It works as expected!
BTW, according to the spec, when there is no min attribute (to determine the step base), but there is a value attribute, then the step base is set to the value of that. In our case this doesn't apply very well, because the value attribute is not compatible with ngModel and is essentially ignored.

Does this PR introduce a breaking change?
No

Please check if the PR fulfills these requirements

Other information:

  • Needs to be backported to 1.5.x for [ng-input-range].

Fixes 9a8b8aa#commitcomment-19108436
Fixes #15257

@gkalpak gkalpak modified the milestones: 1.5.x, 1.5.9 Oct 13, 2016
gkalpak referenced this pull request Oct 14, 2016
Step support works like min / max, but with the following caveat.
Currently, only Firefox fully implements the spec. Other browsers
(Chrome, Safari, Edge) have issues when the step value changes
after the input has been changed. They do not adjust the input value
to a valid value, but instead set the stepMismatch validity state.

Angular will take this validity state, and forward it as the ngModel
"step" error. Adjusting the error ourselves would add too much code,
as the logic is quite involved.
@gkalpak gkalpak force-pushed the fix-step-validation branch from 0083fc1 to bed92aa Compare October 14, 2016 21:56
@gkalpak gkalpak force-pushed the fix-step-validation branch from bed92aa to 10870c8 Compare October 14, 2016 22:00
@@ -2748,7 +2749,8 @@ describe('input', function() {
expect($rootScope.value).toBe(30);

// 0.3 - 0.2 === 0.09999999999999998
$rootScope.$apply('min = 0.2; step = 0.09999999999999998; value = 0.3');
$rootScope.$apply('min = 0.2; step = (0.3 - 0.2)');
helper.changeInputValueTo('0.3');
Copy link
Member Author

Choose a reason for hiding this comment

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

Not changing the scope value (e.g. $rootScope.$apply('value = '0.3')), because ngModel does not overwrite programmatically changed scope values unless the validity of the input changes. Depending on the order in which the validators are run and the previous state, this may lead to the scope value remaining 0.3 instead of being set to undefined.

expect(inputElm).toBeValid();
expect($rootScope.value).toBe(20);
expect($rootScope.form.alias.$error.step).toBeFalsy();
forEach({
Copy link
Contributor

Choose a reason for hiding this comment

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

why not they?

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 want to repeat the describe block. they is for repeating it blocks.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see. Problem is you won't be able to see if ngStep or step failed, because the its have the same name. Maybe put the attribute name in the its?

Copy link
Member Author

Choose a reason for hiding this comment

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

It will be in describe's...description, so it is visible.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, that's right!

expect($rootScope.value).toBe(10);
expect(inputElm.val()).toBe('10');
expect($rootScope.form.alias.$error.step).toBeFalsy();
it('should correctly validate even in cases where `(x*y % x !== 0)`', function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this case an implementation detail? Can we give a better description?

Copy link
Member Author

Choose a reason for hiding this comment

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

It short of is, but value % step === 0 is the obvious way to implement it and it is the exact implementation that leads into errors. I mean we wouldn't have this issues if we had implemented it differently, but we want to explicitly test that the obvious implementation falls short.

That said, I am always open to suggestions 😉

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe "in cases where the JS floating point arithmetic fails"?

Copy link
Member Author

Choose a reason for hiding this comment

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

Works for me.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed.

// and `viewValue` is expected to be a valid stringified number.
var value = Number(viewValue);

// Due to limitations is Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
Copy link
Contributor

Choose a reason for hiding this comment

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

is -> in/of

var value = Number(viewValue);

// Due to limitations is Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
// `0.5 % 0.1 !== 0`), we need to make sure all numbers are integers before proceeding.
Copy link
Contributor

Choose a reason for hiding this comment

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

need to convert all numbers to integers?

@gkalpak
Copy link
Member Author

gkalpak commented Oct 17, 2016

@Narretz, PTAL.

Narretz pushed a commit to Narretz/angular.js that referenced this pull request Oct 17, 2016
@Narretz
Copy link
Contributor

Narretz commented Oct 19, 2016

LGTM

@gkalpak gkalpak closed this in 081d06f Oct 19, 2016
@gkalpak gkalpak deleted the fix-step-validation branch October 19, 2016 12:05
gkalpak added a commit that referenced this pull request Oct 19, 2016
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Nov 21, 2016
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Nov 21, 2016
petebacondarwin added a commit to petebacondarwin/angular.js that referenced this pull request Nov 21, 2016
This reverts commit 90c08b8
feat(input): re-add support for binding to `input[range]`

This commit re-applies the related (previously reverted) commits. A follow-up
commit will make the support opt-in in order to avoid a breaking change.

Included commits:

- 296da4b - `feat(input): add support for binding to input[type=range]`
  (previously reverted with 6a167e8)

- b78539b - `fix(input[range]): correctly handle min/max; remove ngMin/ngMax support`
  (previously reverted with aa60491)

- 90c08b8 - `feat(input[range]): support step`
  (previously reverted with 5b633d8)
fix(input): fix `step` validation for `input[number][ng-range-input]`

Related to 9a8b8aa and angular#15257. Fixes the issue discussed in
angular@9a8b8aa#commitcomment-19108436.

Fixes angular#15257

Closes angular#15264
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Nov 21, 2016
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Nov 21, 2016
petebacondarwin pushed a commit to petebacondarwin/angular.js that referenced this pull request Nov 21, 2016
petebacondarwin pushed a commit that referenced this pull request Nov 23, 2016
petebacondarwin pushed a commit that referenced this pull request Nov 24, 2016
ellimist pushed a commit to ellimist/angular.js that referenced this pull request Mar 15, 2017
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.

[1.5.9] NgModel set value undefined in input number with decimal
3 participants