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

Issues with v-model.number #7136

Open
printercu opened this issue Nov 27, 2017 · 14 comments
Open

Issues with v-model.number #7136

printercu opened this issue Nov 27, 2017 · 14 comments

Comments

@printercu
Copy link

printercu commented Nov 27, 2017

Version

2.5.8

Reproduction link

https://jsfiddle.net/50wL7mdz/79189/

Steps to reproduce

Included in fiddle

What is expected?

Input type='number' should not clear values, accept stings formatted in different locales. v-model.number should not return string.

What is actually happening?

Input value is cleared sometimes, v-model.number returns "" for "" and partially typed numbers.


This issue started with topic on forum https://forum.vuejs.org/t/extra-directive-for-number-inputs/22438, it was suggested to open an issue for that. Here is original post:

Hi!

I've found that v-model.number has limitations and requires some boilerplate in some cases. This is mostly because input[type="number"] returns '' for partially input numbers (like 1.). Here are some problems with it:

  • App has no difference when input is either empty or invalid/partial.
  • Bound attribute has to be of [String, Number] type.
  • '', undefined, and 0 are falsy values. This leads to val !== '' checks in all places this attribute is used.

2nd and 3d issues can be solved with computed property, but it's hard to implement for nested ones and array of values.

I came to using separate field for casted values:

<input
  type='number' 
  v-model='obj.val' 
  @input='$set(obj, "valCasted", _nOrNull($event.target.value))'
/>

I wanted to it implement with custom directive (like v-model-number='obj.valCasted'), but I see that v-model is handled differently by compiler. This way it can automatically use $set when property is not defined on object. But I have not found how this can be implemented with custom directives.

So here are questions :) :

  • Is there a better way to work with input[type="number"]?
    If not:
  • Can this be implemented with custom directives as convenient as v-model is?
  • Should this be added to vue?

After that post I've tried to implement custom component, it's included in fiddle, but it also has some issues.
I've also added different types of inputs to fiddle to check their behaviour and checked against different locales.

Thank you!

@posva
Copy link
Member

posva commented Nov 27, 2017

The number becoming 1 is expected as that's what happens in js, they're the same numbers after all. If you need the zeros, you want a string, not a number. but it happens when the user changes focus which is IMO the ux you want here.

About localization, it's handled by the browser and it's out of vue scope. It's the browser that makes some syntaxes work differently on different computers

About your custom component, it behaves how you told it to behave, it modifies the value and adapt it to either null or the number. The part that may be confusing is that you change it to null when it's not valid, doing backspace changes it back to a valid number and triggers a re render, which is why it changes on the input as well

cc @LinusBorg What was the part that you thought we might improve?

@printercu
Copy link
Author

printercu commented Nov 27, 2017

Thanks for feedback!

I've included plain input[type=number] to fiddle to check it's behaviour in this cases. You can see that it does not clear trailing zeroes at all. Actually I also don't see a problem in stripping trailing zeroes on blur, but I think it should not clear them when changing 1.001 to 1.002. It's the 1) and 4) cases in fiddle.

Original post was more about how to make v-model.number to cast values to Number|null but not Number|String as it is now. I've made some proposal but it's not clear for me how to implement it. As I understood, Linus said that it may be good if vue provide this feature itself. Please check 'Here is original post: Hi! I've found that... ' in first comment for more info.

@printercu
Copy link
Author

printercu commented Nov 27, 2017

For now I don't see how it can be done without having 2 separate fields: 1 - string to keep input[type=number] fine, and 2 - numeric for app. Is it possible somehow to prevent rerender of input on input event? I think this could solve all the issues.

@posva
Copy link
Member

posva commented Nov 27, 2017

but I think it should not clear them when changing 1.001 to 1.002.

That only happens with the custom input. The explanation is here:

The part that may be confusing is that you change it to null when it's not valid, doing backspace changes it back to a valid number and triggers a re render, which is why it changes on the input as well

v-model.number will cast values to Number or empty string, but you're of course free to create your own custom component that handles things differently using or not the number modifier

@printercu
Copy link
Author

That only happens with the custom input

Not only, see 1) case in fiddle (fill 1st input with 1.0000.0 ...), though it's for malformed value.

but you're of course free to create

So is it not interesting to vue team to provide more type-strict interface? It's error prone and such errors are easy to leave, because there are no exceptions on misuse. Also to check that input is empty it's not enough to val != null but it also requires val != null && val !== '' in every place it's used.

@posva
Copy link
Member

posva commented Nov 27, 2017

It does not happen with 1), or I'm not getting the instructions(typing 1.0000.0, then backspace x2).

it also requires val != null && val !== '' in every place it's used.

No, it does not. It's either a number or '' no null. In your example, it's null because that's the initial value you're giving to it
Vue's job here is to link the value of the input to the state and make it a number whenever possible because of the number modifier.
The empty string is actually very useful because it allows you know if the input contains a valid number, it makes it flexible to use in different scenarios

@printercu
Copy link
Author

No, it does not.

Here is example: https://jsfiddle.net/hbov4mmr/8/
Checks item.value != null && item.value !== '' are repeated 3 times.

The empty string is actually very useful

I'm not sure it's useful at all, because there is same value for empty string and for invalid value. In locales with , delimiter 1. is invalid (returns '') but 1.2 is valid and returns 1.2. So empty string has ambiguous, very different meanings.

@posva
Copy link
Member

posva commented Nov 27, 2017

Your example is completely different: the value is not initialized, it's as if you explicitly set it to undefined. Checking if an input is dirty or not is yet, another different thing, unrelated to numbers

I'm not sure it's useful at all, because there is same value for empty string and for invalid value. In locales with , delimiter 1. is invalid (returns '') but 1.2 is valid and returns 1.2. So empty string has ambiguous, very different meanings.

That's the browser setting it to an empty string... Number validation is out of Vue scope

Anyway, just waiting for @LinusBorg input on what could be improved

@printercu
Copy link
Author

printercu commented Nov 28, 2017

Once again, this all is not about checking if an input is dirty, but about providing more solid and type-strict interface: if it returns numbers it should not return empty string or any other string. Returning empty string now is just a workaround, and i think it can be fixed.

Can we please treat it more as feature request and ask few more maintainers look at it.

I also wonder why I can not implement v-model.number in "user-space": https://jsfiddle.net/rubx2fcb/1/ Here I took all the code from internals (https://github.com/vuejs/vue/blob/dev/src/platforms/web/compiler/directives/model.js) but it still does not allow to input 1.0 into this input. Am I missing something?

UPD. I was checking in chrome. In safari It allows to input 1.0 or 1,0 but clears trailing zeroes when removing last digit in 1.0001. FF does not respect OS locale settings and ignores 1,23 formatted numbers, while still has issue with removing last digit in 1.0001.

@lbogdan
Copy link
Contributor

lbogdan commented Dec 4, 2017

@printercu Here's a try at making a numeric input component: https://codesandbox.io/embed/2wrqj87q0y?module=%2FApp.vue . It uses NaN as the empty / not valid value. The only issue it has is that if you typed an invalid number, you can't clear it by setting the v-model value to NaN, as it can't distinguish between empty and not valid.

@printercu
Copy link
Author

@lbogdan Looks great! Thank you! I've slightly changed and it also works with null instead of NaN: https://codesandbox.io/embed/4wk0k64yw0. I will give it a try in the app soon.

Can you please help with (from my previous comment):

I also wonder why I can not implement v-model.number in "user-space": https://jsfiddle.net/rubx2fcb/1/ Here I took all the code from internals (https://github.com/vuejs/vue/blob/dev/src/platforms/web/compiler/directives/model.js) but it still does not allow to input 1.0 into this input. Am I missing something?

@vuejs vuejs deleted a comment from networkimprov Aug 6, 2018
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 11, 2020
 - Reduces the number of times the UI is generating access tokens
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 11, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 11, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 11, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 11, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 11, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 12, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
jamesisaacs pushed a commit to GovWizely/loc-sas that referenced this issue Mar 13, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
jamesisaacs pushed a commit to GovWizely/loc-sas that referenced this issue Mar 13, 2020
 - Reduced the number of times the UI is generating access tokens
 - Simplified sub-forms; removed unnecesary field declarations
 - Saving numbers is still an issue: vuejs/vue#7136
 - TODO: Find a way to save integers by preventing them from being saved as strings
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 24, 2020
- All optional numeric fields are updated to null when they're an empty string
- Scenarios only affects optional fields; it only happens when number fields are cleared
- vuejs/vue#7136
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 24, 2020
- All optional numeric fields are updated to null when they're an empty string
- The scenario only affects optional fields; it happens when number fields are cleared
- vuejs/vue#7136
lipsotiko pushed a commit to GovWizely/loc-sas that referenced this issue Mar 24, 2020
- All optional numeric fields are updated to null when they're an empty string
- The scenario only affects optional fields; it happens when number fields are cleared
- vuejs/vue#7136
jamesisaacs pushed a commit to GovWizely/loc-sas that referenced this issue Mar 24, 2020
- All optional numeric fields are updated to null when they're an empty string
- The scenario only affects optional fields; it happens when number fields are cleared
- vuejs/vue#7136
jamesisaacs pushed a commit to GovWizely/loc-sas that referenced this issue Mar 24, 2020
- All optional numeric fields are updated to null when they're an empty string
- The scenario only affects optional fields; it happens when number fields are cleared
- vuejs/vue#7136
@rmirabelle
Copy link

With v-model.number happily returning "" instead of null, for empty values, I'm not sure what the value of v-model.number even is. Isn't the entire point of the directive to ensure that the value is actually a number? If not, I would argue it's named inappropriately.

@Nicate
Copy link

Nicate commented Jun 23, 2022

At the moment v-model.number doesn't even produce a number if there is text in the input. It only works if the first character is a number (only tested this in Firefox).

It's easy to reproduce, just go to https://vuejs.org/guide/essentials/forms.html#basic-usage and click "Try it in the Playground" and add .number to v-model. Then just type text in the "edit me" field.

@lukeJEdwards
Copy link

v-model.number still doesn't work in firefox 125.0.2 but does in Chrome

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

7 participants