From bf4a56669c2a8eff05a514ab2d200ff2b8f409ec Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Tue, 8 Oct 2019 03:08:18 +0200 Subject: [PATCH] fix: handle `v-model.trim` and `v-model.number` (#2408) * fix: cast values to numbers before commiting them closes #2403 * fix: trim values before commiting them when v-model.trim is used * test: added modifier tests * test: added case for NaN with v-model.number --- src/utils/events.ts | 25 ++++++++++++++++++-- tests/providers/provider.js | 46 +++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/utils/events.ts b/src/utils/events.ts index 5fa06fb5d..1a307082e 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -1,4 +1,4 @@ -import { isCallable, toArray } from './index'; +import { isCallable, toArray, isNaN } from './index'; export const isEvent = (evt: any): evt is Event => { if (!evt) { @@ -18,15 +18,36 @@ export const isEvent = (evt: any): evt is Event => { return false; }; +type BoundInputElement = HTMLInputElement & { + _vModifiers?: { number?: boolean; trim?: boolean }; +}; + export function normalizeEventValue(value: unknown): any { if (!isEvent(value)) { return value; } - const input = value.target as HTMLInputElement; + const input = value.target as BoundInputElement; if (input.type === 'file' && input.files) { return toArray(input.files); } + // If the input has a `v-model.number` modifier applied. + if (input._vModifiers && input._vModifiers.number) { + // as per the spec the v-model.number uses parseFloat + const valueAsNumber = parseFloat(input.value); + if (isNaN(valueAsNumber)) { + return input.value; + } + + return valueAsNumber; + } + + if (input._vModifiers && input._vModifiers.trim) { + const trimmedValue = typeof input.value === 'string' ? input.value.trim() : input.value; + + return trimmedValue; + } + return input.value; } diff --git a/tests/providers/provider.js b/tests/providers/provider.js index 2643777e0..6b47ff830 100644 --- a/tests/providers/provider.js +++ b/tests/providers/provider.js @@ -794,7 +794,7 @@ test('validates manually with a initial value using the validate event handler o

{{ errors[0] }}

- + ` }, { localVue: Vue, sync: false } @@ -819,7 +819,7 @@ test('validates manually with a initial value using the validate event handler o

{{ errors[0] }}

- + ` }, { localVue: Vue, sync: false } @@ -1110,3 +1110,45 @@ test('returns custom error messages passed in the customMessages prop', async () expect(result.errors[0]).toEqual(customMessage); }); + +describe('Handle value mutating modifiers', () => { + test('handles .number modifier', () => { + const wrapper = mount( + { + data: () => ({ val: '' }), + template: ` + + + + ` + }, + { localVue: Vue, sync: false } + ); + + // should happen synchronously! + wrapper.find('input').setValue('11'); + expect(wrapper.vm.$refs.provider.value).toBe(11); + + // NaN values are left as is. + wrapper.find('input').setValue('x23'); + expect(wrapper.vm.$refs.provider.value).toBe('x23'); + }); + + test('handles .trim modifier', () => { + const wrapper = mount( + { + data: () => ({ val: '' }), + template: ` + + + + ` + }, + { localVue: Vue, sync: false } + ); + + // should happen synchronously! + wrapper.find('input').setValue(' abc'); + expect(wrapper.vm.$refs.provider.value).toBe('abc'); + }); +});