]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(v-model): built in modifiers support on component (#2348)
authorHunter <kltan69@gmail.com>
Tue, 20 Oct 2020 13:59:27 +0000 (21:59 +0800)
committerGitHub <noreply@github.com>
Tue, 20 Oct 2020 13:59:27 +0000 (09:59 -0400)
close #2326

packages/runtime-core/__tests__/componentEmits.spec.ts
packages/runtime-core/src/componentEmits.ts

index f93d547ab19aea3a9e3fe8377694949595d7ff87..53c8d324ea307a8638c7d09459a9da5615630020 100644 (file)
@@ -220,6 +220,68 @@ describe('component: emit', () => {
     expect(onFooOnce).toHaveBeenCalledTimes(1)
   })
 
+  test('.number modifier should work with v-model on component', () => {
+    const Foo = defineComponent({
+      render() {},
+      created() {
+        this.$emit('update:modelValue', '1')
+        this.$emit('update:foo', '2')
+      }
+    })
+
+    const fn1 = jest.fn()
+    const fn2 = jest.fn()
+
+    const Comp = () =>
+      h(Foo, {
+        modelValue: null,
+        modelModifiers: { number: true },
+        'onUpdate:modelValue': fn1,
+
+        foo: null,
+        fooModifiers: { number: true },
+        'onUpdate:foo': fn2
+      })
+
+    render(h(Comp), nodeOps.createElement('div'))
+
+    expect(fn1).toHaveBeenCalledTimes(1)
+    expect(fn1).toHaveBeenCalledWith(1)
+    expect(fn2).toHaveBeenCalledTimes(1)
+    expect(fn2).toHaveBeenCalledWith(2)
+  })
+
+  test('.trim modifier should work with v-model on component', () => {
+    const Foo = defineComponent({
+      render() {},
+      created() {
+        this.$emit('update:modelValue', ' one ')
+        this.$emit('update:foo', '  two  ')
+      }
+    })
+
+    const fn1 = jest.fn()
+    const fn2 = jest.fn()
+
+    const Comp = () =>
+      h(Foo, {
+        modelValue: null,
+        modelModifiers: { trim: true },
+        'onUpdate:modelValue': fn1,
+
+        foo: null,
+        fooModifiers: { trim: true },
+        'onUpdate:foo': fn2
+      })
+
+    render(h(Comp), nodeOps.createElement('div'))
+
+    expect(fn1).toHaveBeenCalledTimes(1)
+    expect(fn1).toHaveBeenCalledWith('one')
+    expect(fn2).toHaveBeenCalledTimes(1)
+    expect(fn2).toHaveBeenCalledWith('two')
+  })
+
   test('isEmitListener', () => {
     const options = { click: null }
     expect(isEmitListener(options, 'onClick')).toBe(true)
index 726a16b87435ec0f2eac852834ac7ac273fac253..6b9e505de6299947ac562d5334c311762c9d4525 100644 (file)
@@ -7,7 +7,8 @@ import {
   hyphenate,
   isArray,
   isFunction,
-  isOn
+  isOn,
+  toNumber
 } from '@vue/shared'
 import {
   ComponentInternalInstance,
@@ -45,7 +46,7 @@ export type EmitFn<
 export function emit(
   instance: ComponentInternalInstance,
   event: string,
-  ...args: any[]
+  ...rawArgs: any[]
 ) {
   const props = instance.vnode.props || EMPTY_OBJ
 
@@ -65,7 +66,7 @@ export function emit(
       } else {
         const validator = emitsOptions[event]
         if (isFunction(validator)) {
-          const isValid = validator(...args)
+          const isValid = validator(...rawArgs)
           if (!isValid) {
             warn(
               `Invalid event arguments: event validation failed for event "${event}".`
@@ -76,6 +77,23 @@ export function emit(
     }
   }
 
+  let args = rawArgs
+  const isModelListener = event.startsWith('update:')
+
+  // for v-model update:xxx events, apply modifiers on args
+  const modelArg = isModelListener && event.slice(7)
+  if (modelArg && modelArg in props) {
+    const modifiersKey = `${
+      modelArg === 'modelValue' ? 'model' : modelArg
+    }Modifiers`
+    const { number, trim } = props[modifiersKey] || EMPTY_OBJ
+    if (trim) {
+      args = rawArgs.map(a => a.trim())
+    } else if (number) {
+      args = rawArgs.map(toNumber)
+    }
+  }
+
   if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
     devtoolsComponentEmit(instance, event, args)
   }
@@ -101,7 +119,7 @@ export function emit(
   let handler = props[handlerName]
   // for v-model update:xxx events, also trigger kebab-case equivalent
   // for props passed via kebab-case
-  if (!handler && event.startsWith('update:')) {
+  if (!handler && isModelListener) {
     handlerName = toHandlerKey(hyphenate(event))
     handler = props[handlerName]
   }