Преглед изворни кода

fix(v-model): built in modifiers support on component (#2348)

close #2326
Hunter пре 5 година
родитељ
комит
128ec460ec

+ 62 - 0
packages/runtime-core/__tests__/componentEmits.spec.ts

@@ -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)

+ 22 - 4
packages/runtime-core/src/componentEmits.ts

@@ -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]
   }