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

Bug fixes on v-text-field #2099

Merged
merged 11 commits into from Oct 18, 2017
Merged

Bug fixes on v-text-field #2099

merged 11 commits into from Oct 18, 2017

Conversation

@azaars
Copy link
Contributor Author

azaars commented Oct 9, 2017

Ups... It breaks some of the tests.

I think those tests are no longer applicable and should be replaced with something else.

@KaelWD
Copy link
Member

KaelWD commented Oct 9, 2017

It breaks one of the tests. Have you checked that clearable still works after these changes?

@azaars
Copy link
Contributor Author

azaars commented Oct 9, 2017

Ok, will do so.

// maskText() masks and removes dirty alphanum
// Outer unmaskText() provides a clean lazyValue
this.lazyValue = this.unmaskText(this.maskText(this.unmaskText(val)))
this.setSelectionRange()
Copy link
Member

Choose a reason for hiding this comment

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

What about non-masked inputs? This will probably break text fields with type="number" as they don't support setSelectionRange().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did look at non-mask input. I even did some tests on first with a mask then later removed it on the fly.

Yeah you're probably right about type="number". Well, it's not really related to the issues this PR is trying to resolve. There are several other issues that exist even before this PR, which are overlooked yet to surface sooner or later. IMHO lets handle these separately as different issues.

Copy link
Member

Choose a reason for hiding this comment

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

I mean type="number" in general. Currently setSelectionRange is only called if a mask is set:

this.mask && this.setSelectionRange()

What happens with this patch if you just have a number input without a mask?

<v-text-field v-model="number" type="number">

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I see what you mean.

Copy link
Member

Choose a reason for hiding this comment

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

You can probably just put the this.mask && back in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Placing back merely the this.mask && caused the clearable test to fail once again. This time it's expecting an emit().

Admittedly, I did not give much attention to the totally valid null value for input field before. So now I've fixed some more lines here and there. For the same reason, the extra check needs to be there once again @jacekkarczmarczyk :

if (!masked.length || !text || !text.length) return text

@azaars
Copy link
Contributor Author

azaars commented Oct 9, 2017

Yeay the test passed :-)

src/util/mask.js Outdated
@@ -90,7 +90,7 @@ const maskValidates = (mask, char) => {
* @return {String}
*/
export const maskText = (text, masked, dontFillMaskBlanks) => {
if (!masked.length || !text.length) return text
if (!masked.length || !text || !text.length) return text
Copy link
Member

Choose a reason for hiding this comment

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

if (!masked.length || !text) return text should be enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Really? I'm already too deep into strongly typed language hahaa.

Thanks @jacekkarczmarczyk

@johnleider
Copy link
Member

Tests need to be written for each issue.

@azaars
Copy link
Contributor Author

azaars commented Oct 9, 2017

I'm sorry. I'm not familiar with the test tools at all. Will surely explore but for now it would take me ages to do that.

@johnleider
Copy link
Member

Sorry, response was mainly for myself to remember to do it :)

if (oldText) {
for (const char of oldText.substr(0, selection)) {
isMaskDelimiter(char) || position++
}
Copy link
Member

Choose a reason for hiding this comment

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

oldText will always be a string, this is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, that's because I expect null to be a valid value, and that this.lazyValue has to be able to accept null.

Copy link
Member

Choose a reason for hiding this comment

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

oldText is directly from the html element, which is always a string, even if you set it to null first.

Copy link
Member

Choose a reason for hiding this comment

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

This is still redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed it is. input.value never becomes null. Even if it's explicitly set to null, it turns to an empty string. At least in the latest Firefox. I'm changing it right away.

- Dev should be allowed to set input to null and expect v-model to
  retain the null value. Requesting a reconsideration against returning
a string as in commit 95b7f83

- Handle v-model.number modifier correctly. Whether the input is string,
  number or null, the resulting emit() should return the same type or
null. Requesting this as an alternative to resolve vuetifyjs#2137.

- Handle input of type number. Likewise, the resulting emit() should
  return the same type. Requesting this as an alternative to resolve vuetifyjs#2136.
src/util/mask.js Outdated
if (text == null) return ''
text = String(text)
if (!masked.length || !text.length) return text
if (!masked.length || !text) return text
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just found out that !'' === true, so !text.length not only is redundant but will fail when text === null. Still learning 😊

@KaelWD KaelWD self-requested a review October 12, 2017 01:07
this.lazyValue = typeof val === 'number' ? +value : value
this.mask ? this.setSelectionRange()
: this.$emit('input', this.returnMaskedValue
? this.$refs.input.value : this.lazyValue)
Copy link
Member

@KaelWD KaelWD Oct 12, 2017

Choose a reason for hiding this comment

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

Why are we emitting different values based on returnMaskedValue when there is no mask?

Copy link
Member

Choose a reason for hiding this comment

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

Also, this whole function should probably be split by an if statement so none of this casting is happening on non-masked inputs. The same applies for the value watcher too.

@@ -90,14 +90,15 @@ const maskValidates = (mask, char) => {
* @return {String}
Copy link
Member

Choose a reason for hiding this comment

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

This docblock needs updating if you're returning arbitrary values. Does jsdoc have generics?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You know better than I do.

Copy link
Member

Choose a reason for hiding this comment

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

@azaars
Copy link
Contributor Author

azaars commented Oct 13, 2017

Please let me know if I miss anything else.

@KaelWD KaelWD added pending review The issue is still pending disposition and removed pending merge labels Oct 14, 2017
src/util/mask.js Outdated
@@ -90,14 +90,15 @@ const maskValidates = (mask, char) => {
* @return {String}
*/
export const maskText = (text, masked, dontFillMaskBlanks) => {
if (text == null) return ''
text = String(text)
Copy link
Member

Choose a reason for hiding this comment

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

These lines were here for a reason. The value should still be cast to a string before calculating the cursor position, because that's what's going to be set on the input in the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing if (text == null) return was intentional because I asked to reconsider having lazyValue to accept null. I described my point here 95b7f83 . Does this mean you not buying the idea?

As for line text = String(text), I didn't remove it. You can find at in line 100, which I think looks more tidy without affecting the logic.

Copy link
Member

@KaelWD KaelWD Oct 14, 2017

Choose a reason for hiding this comment

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

I think accepting null is fine, but completely removing this means that the cursor position is wrong if the mask changes when the value is falsy (0 with v-model.number for example). You should be casting newText to string (with a proper null check) in updateRange() and watch.mask() before the loop. This will also allow the removal of the ifs, because for..of on an empty string just gets skipped.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can see your point now. All codes that requires the return value of maskText() are expecting a string. So, converting the falsy to '0' is indeed correct. I'm reverting to your last commit.

Copy link
Member

@KaelWD KaelWD Oct 15, 2017

Choose a reason for hiding this comment

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

if (text == null) return text
text = String(text)

Might work to keep null values

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, now I prefer your last commit. Like I said, all readers of maskText() including newValue and domProps.value are expecting string.

this.lazyValue = val
this.$emit('input', this.lazyValue)
},
getLazyValue (val) {
Copy link
Member

Choose a reason for hiding this comment

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

This name is bad, it doesn't accurately describe what the function is doing. Is this method even needed? You only use it in one place.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's used in 2 places. The other one is at line 92 of VTextField.js. Do you prefer not to have this this function? I'll just put the content where it was before.

Copy link
Member

Choose a reason for hiding this comment

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

What is it actually doing anyway? It looks like it just casts val to a number when it already is one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually it casts to the type that the dev enters in props.value

Copy link
Member

Choose a reason for hiding this comment

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

It was here, but now it does nothing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OMG what was I thinking when I did that.

You're damn sharp man. I salute

},
updateOnValueChange (val) {
this.lazyValue = val
this.$emit('input', this.lazyValue)
Copy link
Member

Choose a reason for hiding this comment

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

This is causing input to be emitted twice, as you're also doing it in both inputValue.set() and updateRange()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, actually I didn't quite get it when you said:

Also, this whole function should probably be split by an if statement so none of this casting is happening on non-masked inputs. The same applies for the value watcher too.

I understood it as having if statement to call different functions. Maybe because I didn't understand what you meant about casting issue anywhere in that function. Frankly I also don't like the changes I made and in fact I don't like the new function names I gave. Perhaps you would want to elaborate your suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On multiple emit problem, I realize that the watch.value has never been completed for what it's supposed to function as. From the comment, it should handle external change but there is nothing in the function to detect whether the change is external or internal. So, there have always been multiple emits from internally. I'm going to address this in a later PR.

Copy link
Member

Choose a reason for hiding this comment

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

I said that because this.unmaskText(this.maskText(this.unmaskText(val))) was being run on every change, even on non-masked inputs.

Copy link
Member

Choose a reason for hiding this comment

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

there have always been multiple emits from internally

<v-text-field :mask="####" v-model.number="num"/>

Click inside, type 1, blur.

On dev:
image

On this branch:
image

this.lazyValue = this.getLazyValue(value)
this.$nextTick(() => {
this.$refs.input.value = masked
this.$emit('input', this.lazyValue)
Copy link
Member

Choose a reason for hiding this comment

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

This one too.

@azaars
Copy link
Contributor Author

azaars commented Oct 14, 2017

What exactly does codeclimate means in the last commit? Where in the functions does it consider it as too complex?

@johnleider
Copy link
Member

Don't worry about codeclimate for now

- Revert to last commit to undo unnecessary changes
- Fix computed.inputValue to account for type of emitted value
- Enhance watch.value to check for external/internal change
- Prevent resetSelections() from causing runtime error when
  type="number'
- Fix multiple emits bug
- Minor change to local variable names to be consistent between
  functions
@azaars
Copy link
Contributor Author

azaars commented Oct 15, 2017

@KaelWD I've commited the post-review changes.

@KaelWD KaelWD added this to the v0.17.x milestone Oct 15, 2017
@johnleider johnleider removed the pending review The issue is still pending disposition label Oct 18, 2017
@johnleider johnleider merged commit 54e2b78 into vuetifyjs:dev Oct 18, 2017
@azaars azaars deleted the fix/#2057 branch October 26, 2017 04:04
this.lazyValue = typeof val === 'number' ? +value : value

// Emit when the externally set value was modified internally
val !== this.lazyValue && this.$nextTick(() => {
Copy link
Member

Choose a reason for hiding this comment

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

why the nextTick here?

}

this.setCaretPosition(selection)
this.$nextTick(() => {
Copy link
Member

Choose a reason for hiding this comment

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

and here too I guess

@lock
Copy link

lock bot commented Apr 15, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please direct any non-bug questions to our Discord

@lock lock bot locked as resolved and limited conversation to collaborators Apr 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants