v-mask
v-mask copied to clipboard
Passing function as mask causes infinite loop when used with vee-validate
V-Mask and Vue versions
2.2.4, 2.6.12
Reproduction Link
https://codesandbox.io/s/v-mask-demo-forked-ch575?file=/components/Input.vue
Steps to reproduce
- Open the link
- Observe the console,
input
events get continously emitted causing re-renders
What is Expected?
Adding function expression as mask should not trigger input
events on every componentUpdated
What is actually happening?
I am not quite sure what exacly happens there, but I noticed that if I remove v-slot
(scoped slot) from ValidationObserver
then the problem disappears. Not sure why, but seems like v-mask componentUpdated
triggers new input
event on every component re-render which then gets listened by ValidationObserver
which re-renders slotted content and v-mask again triggers input
event causing a loop. Is there any specific reason why the componentUpdated
method does not compare function results ? I mean this code.
var isMaskChanged = isFunction(value) || maskToString(oldValue) !== maskToString(value);
What is the purpose of marking the mask as changed if isFunction(value)
?
Same issue without vee-validate
Hiya folks! If you're visiting this in the future, here's how I managed to solve this problem:
You /can not/ use a computed property OR pass a function directly to v-mask
. You must use a regular variable, and then update that variable with watch
. For example:
<input v-mask='mask' v-model='mymodel' />
...
data() {
return {
mymodel: '',
mask: '',
currencyMask: createNumberMask({})
}
}
watch() {
mymodel(newVal) {
mask = this.currencyMask(newVal)
}
}
Hiya folks! If you're visiting this in the future, here's how I managed to solve this problem:
You /can not/ use a computed property OR pass a function directly to
v-mask
. You must use a regular variable, and then update that variable withwatch
. For example:<input v-mask='mask' v-model='mymodel' /> ... data() { return { mymodel: '', mask: '', currencyMask: createNumberMask({}) } } watch() { mymodel(newVal) { mask = this.currencyMask(newVal) } }
This will solve most things but will still trip when using currency mask with decimal, v-mask's parseMask
function when processing different type of mask, i.e function, array or string will differs.
Function will be used as is, but with array, if an element is string, it will split into a char array, which will mess with text-mask-addons "caret capture" string https://github.com/text-mask/text-mask/blob/master/addons/src/createNumberMask.js#L137
Our team avoided this issue by filtering out that specific string, which is '[]'.