diff --git a/packages/runtime-core/src/componentEmits.ts b/packages/runtime-core/src/componentEmits.ts index db52bc88c33..7e4de02ad84 100644 --- a/packages/runtime-core/src/componentEmits.ts +++ b/packages/runtime-core/src/componentEmits.ts @@ -32,6 +32,7 @@ import { import type { ComponentTypeEmits } from './apiSetupHelpers' import { getModelModifiers } from './helpers/useModel' import type { ComponentPublicInstance } from './componentPublicInstance' +import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig' export type ObjectEmitsOptions = Record< string, @@ -151,10 +152,14 @@ export function emit( } let args = rawArgs - const isModelListener = event.startsWith('update:') + const isModelListener = + __COMPAT__ && isCompatEnabled(DeprecationTypes.COMPONENT_V_MODEL, instance) + ? compatModelEventPrefix + event in props + : event.startsWith('update:') // for v-model update:xxx events, apply modifiers on args - const modifiers = isModelListener && getModelModifiers(props, event.slice(7)) + const modifiers = + isModelListener && getModelModifiers(instance, props, event.slice(7)) if (modifiers) { if (modifiers.trim) { args = rawArgs.map(a => (isString(a) ? a.trim() : a)) diff --git a/packages/runtime-core/src/helpers/useModel.ts b/packages/runtime-core/src/helpers/useModel.ts index c40938ead3c..f8edd60c995 100644 --- a/packages/runtime-core/src/helpers/useModel.ts +++ b/packages/runtime-core/src/helpers/useModel.ts @@ -2,9 +2,11 @@ import { type Ref, customRef, ref } from '@vue/reactivity' import { EMPTY_OBJ, camelize, hasChanged, hyphenate } from '@vue/shared' import type { DefineModelOptions, ModelRef } from '../apiSetupHelpers' import { getCurrentInstance } from '../component' +import type { ComponentInternalInstance } from '../component' import { warn } from '../warning' import type { NormalizedProps } from '../componentProps' import { watchSyncEffect } from '../apiWatch' +import { DeprecationTypes, isCompatEnabled } from '../compat/compatConfig' export function useModel< M extends PropertyKey, @@ -35,7 +37,7 @@ export function useModel( } const hyphenatedName = hyphenate(name) - const modifiers = getModelModifiers(props, camelizedName) + const modifiers = getModelModifiers(i, props, camelizedName) const res = customRef((track, trigger) => { let localValue: any @@ -118,9 +120,17 @@ export function useModel( } export const getModelModifiers = ( + instance: ComponentInternalInstance, props: Record, modelName: string, ): Record | undefined => { + if ( + __COMPAT__ && + isCompatEnabled(DeprecationTypes.COMPONENT_V_MODEL, instance) + ) { + return props.modelModifiers + } + return modelName === 'modelValue' || modelName === 'model-value' ? props.modelModifiers : props[`${modelName}Modifiers`] || diff --git a/packages/vue-compat/__tests__/componentVModel.spec.ts b/packages/vue-compat/__tests__/componentVModel.spec.ts index 7e498828715..5ef19954734 100644 --- a/packages/vue-compat/__tests__/componentVModel.spec.ts +++ b/packages/vue-compat/__tests__/componentVModel.spec.ts @@ -82,4 +82,62 @@ describe('COMPONENT_V_MODEL', () => { template: ``, }) }) + + async function runTestWithModifier(CustomInput: ComponentOptions) { + const vm = new Vue({ + data() { + return { + text: ' foo ', + } + }, + components: { + CustomInput, + }, + template: ` +
+ {{ text }} + +
+ `, + }).$mount() as any + + const input = vm.$el.querySelector('input') + const span = vm.$el.querySelector('span') + + expect(input.value).toBe(' foo ') + expect(span.textContent).toBe(' foo ') + + expect( + (deprecationData[DeprecationTypes.COMPONENT_V_MODEL].message as Function)( + CustomInput, + ), + ).toHaveBeenWarned() + + input.value = ' bar ' + triggerEvent(input, 'input') + await nextTick() + + expect(input.value).toBe('bar') + expect(span.textContent).toBe('bar') + } + + test('with model modifiers', async () => { + await runTestWithModifier({ + name: 'CustomInput', + props: ['value'], + template: ``, + }) + }) + + test('with model modifiers and model option', async () => { + await runTestWithModifier({ + name: 'CustomInput', + props: ['foo'], + model: { + prop: 'foo', + event: 'bar', + }, + template: ``, + }) + }) })