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

Number field fires spurious value change events when assigning a value that cannot be parsed #8007

Open
Legioth opened this issue Oct 23, 2024 · 6 comments

Comments

@Legioth
Copy link
Member

Legioth commented Oct 23, 2024

Description

If I assign a value that cannot be parsed as a number to the number field, then then the actual is set as the empty string. In the process, the field fires two value-changed events where event.detail.value is the empty string. This happens even if the field was previously empty which means that there's no user-observable change to the field.

This is particularly problematic when binding the field to a state variable typed as number. A trivial implementation would use parseInt(event.detail.value) which for an empty input gives NaN as the value. This is in itself a bit weird but still reasonable. The problem comes when assigning the value back to the field as '' + value since that assigns 'NaN' as the value and this triggers those spurious events. This might in turn lead to an infinite loop if the value change event is in turn used to update the state in a way that doesn't take into account that NaN == NaN evaluates to false in JavaScript (e.g. with a Preact signal: preactjs/signals#614).

Expected outcome

No event should fired since the effective value is '' both before and after the change.

I could also understand if the first event would have the assigned unparseable string as event.detail.value and then there would be a second value for changing back to '' but that would not be practical since it leads to even worse event loops.

Minimal reproducible example

https://vaadin.com/docs/latest/components/number-field

Steps to reproduce

  1. Open https://vaadin.com/docs/latest/components/number-field
  2. Select any number field from the page in the browser's inspector (be careful to select the web component and not the <input>)
  3. Add an event listener through the JS console: $0.addEventListener('value-changed', event => console.log(event.detail.value))
  4. Change the value to any unparseable string: $0.value = "NaN"
  5. Note the two events logged to the console

Environment

Vaadin version(s): 24.6.0-alpha2

Browsers

No response

@vursen
Copy link
Contributor

vursen commented Oct 23, 2024

Related #4184

@TatuLund
Copy link
Contributor

A bit more distant cousin here: vaadin/hilla#2185

@rolfsmeds
Copy link
Contributor

@vursen, could this be prevented without doing #4184?

@vursen
Copy link
Contributor

vursen commented Oct 28, 2024

could this be prevented without doing #4184?

Yes, seems to be doable by moving the type conversion logic from the value observer to the value setter:

_valueChanged(newVal, oldVal) {
-  // Validate value to be numeric
-  if (newVal && isNaN(parseFloat(newVal))) {
-    this.value = '';
-  } else if (typeof this.value !== 'string') {
-    this.value = String(this.value);
-  }

  super._valueChanged(this.value, oldVal);
}

+ get value() {
+  return super.value;
+ }

+ set value(value) {
+   if (isNaN(parseFloat(value))) {
+     super.value = '';
+   } else if (typeof value !== 'string') {
+     super.value = String(value);
+  } else {
+     super.value = value;
+  }
+ }

@vursen
Copy link
Contributor

vursen commented Oct 30, 2024

Though, there is still a question of whether we should show a warning when there is an attempt to set an unparsable value. Right now, it's inconsistent: integer-field shows a warning while number-field doesn't.

_valueChanged(newVal, oldVal) {
// Validate value to be numeric
if (newVal && isNaN(parseFloat(newVal))) {
this.value = '';
} else if (typeof this.value !== 'string') {
this.value = String(this.value);
}

_valueChanged(newVal, oldVal) {
if (newVal !== '' && !this.__isInteger(newVal)) {
console.warn(`Trying to set non-integer value "${newVal}" to <vaadin-integer-field>. Clearing the value.`);
this.value = '';
return;
}

I would personally prefer seeing a warning, but I got the impression that setting 'NaN' is planned to be used as a feature.

@Legioth
Copy link
Member Author

Legioth commented Oct 30, 2024

My use of 'NaN' was accidental since I wasn't taking into account that value might not always be an actual number. Logging a warning would have been acceptable and would have made it easier for me to understand the mistake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants