RonaldJerez / vue-input-facade

A lightweight and dependency free input masking library created specific for Vue
https://ronaldjerez.github.io/vue-input-facade/latest/
182 stars 27 forks source link

Input value set in component mounted hook can lead to stale data #65

Open mattheyan opened 2 years ago

mattheyan commented 2 years ago

Describe the bug Apologies in advance for the complicated scenario. If a masked input's value is set after the directive binds, the oldValue property won't be established when the directive binds, which can cause the input's value to be re-used when the component updates due to the underlying data that the input is binding to being updated. I know that's probably hard to follow, so hopefully a more long-winded explanation will help.

The oldValue property is set initially due to a call to updateValue from the bind method of the directive. If the input doesn't have a value yet then oldValue won't be set (and will remain undefined). If a component uses the mounted hook to set the input value programmatically (ex: element's input component), this will happen before the directive is inserted, but after bind, so it won't cause oldValue to be established. If the underlying data changes, the component will update, which will call the directive's update method. Since the oldValue was never established this will cause it to mask the input's value (which has not yet been updated to reflect the change to the data) and raise an input event. It seems like this depends on the exact configuration of components, but the input event may cause the component to capture the value of the input at that time rather than updating to use the new value from the underlying data.

To Reproduce I have tried to reproduce the problem in a codepen, but haven't had luck so far. Unfortunately I can't currently send a demo link in the app where the issue was discovered. Steps to reproduce the behavior:

  1. Use an element input component to bind to some underlying string data, ex: "(800) 123-4567"
  2. Attach the facade directive with a mask, ex: phone number. In my case the directive is attached to an ancestor of the input, but I'm not sure if that matters or not.
  3. Update the underlying data, ex: to blank string
  4. The input in the DOM retains the original value, ex: "(800) 123-4567"

Expected behavior The element in the DOM should be updated to reflect the new value in the underlying data / object.

Desktop (please complete the following information):

Additional context I know you'd like to have a repro, and if I'm able to I will send one. But hopefully its at least possible to see in the code how this could be a problem. In terms of a fix, if oldValue is established when the component mounts (and "inserted" is called), then the input event won't be raised when the component update occurs, which avoids the problem in my scenario. Please let me know if you have any thoughts on this, or better ideas for how to fix it.

Thanks! - Bryan

mattheyan commented 2 years ago

I was able to repro in a test at least, which I will attempt to push to a fork tomorrow.

mattheyan commented 2 years ago

Here's the test where I was able to reproduce the problem. I tried to get it as close as I could to what was I seeing in the app.

RonaldJerez commented 2 years ago

Hey @mattheyan, thanks for the report. I’ll take a look in detail once I get a chance.