From d474accb71826f157b75d3d98ba65eea0d163f21 Mon Sep 17 00:00:00 2001 From: javoski Date: Thu, 17 Dec 2020 21:22:26 +0800 Subject: [PATCH 1/2] feat(v-model): add lazy modifier for v-model on components(close #6914) --- flow/compiler.js | 1 + flow/vnode.js | 1 + src/compiler/codegen/index.js | 2 ++ src/compiler/directives/model.js | 3 ++- src/core/vdom/create-component.js | 2 +- .../features/directives/model-component.spec.js | 17 +++++++++++++++++ test/unit/modules/compiler/codegen.spec.js | 2 +- test/weex/compiler/v-model.spec.js | 2 +- 8 files changed, 26 insertions(+), 4 deletions(-) diff --git a/flow/compiler.js b/flow/compiler.js index ad09a2e2ebe..6cd9d3da177 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -163,6 +163,7 @@ declare type ASTElement = { value: string; callback: string; expression: string; + event: string; }; directives?: Array; diff --git a/flow/vnode.js b/flow/vnode.js index c9f3b3887d3..5a07b1fdf37 100644 --- a/flow/vnode.js +++ b/flow/vnode.js @@ -62,6 +62,7 @@ declare interface VNodeData { model?: { value: any; callback: Function; + event: string; }; }; diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index c96b3a38511..9ffda33b084 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -279,6 +279,8 @@ export function genData (el: ASTElement, state: CodegenState): string { el.model.callback },expression:${ el.model.expression + },event:${ + el.model.event }},` } // inline-template diff --git a/src/compiler/directives/model.js b/src/compiler/directives/model.js index c7a4c09b574..13dd01690eb 100644 --- a/src/compiler/directives/model.js +++ b/src/compiler/directives/model.js @@ -8,7 +8,7 @@ export function genComponentModel ( value: string, modifiers: ?ASTModifiers ): ?boolean { - const { number, trim } = modifiers || {} + const { number, trim, lazy } = modifiers || {} const baseValueExpression = '$$v' let valueExpression = baseValueExpression @@ -26,6 +26,7 @@ export function genComponentModel ( el.model = { value: `(${value})`, expression: JSON.stringify(value), + event: lazy ? `'change'` : `'input'`, callback: `function (${baseValueExpression}) {${assignment}}` } } diff --git a/src/core/vdom/create-component.js b/src/core/vdom/create-component.js index 380902000df..910f0050825 100644 --- a/src/core/vdom/create-component.js +++ b/src/core/vdom/create-component.js @@ -251,7 +251,7 @@ function mergeHook (f1: any, f2: any): Function { // prop and event handler respectively. function transformModel (options, data: any) { const prop = (options.model && options.model.prop) || 'value' - const event = (options.model && options.model.event) || 'input' + const event = (options.model && options.model.event) || data.model.event ;(data.attrs || (data.attrs = {}))[prop] = data.model.value const on = data.on || (data.on = {}) const existing = on[event] diff --git a/test/unit/features/directives/model-component.spec.js b/test/unit/features/directives/model-component.spec.js index 49ff4ddb138..3ae4627bcaa 100644 --- a/test/unit/features/directives/model-component.spec.js +++ b/test/unit/features/directives/model-component.spec.js @@ -149,6 +149,23 @@ describe('Directive v-model component', () => { expect(vm.text).toBe('foo o') }) + it('modifier: .lazy', () => { + const vm = new Vue({ + template: `
`, + data: { text: 'foo' }, + components: { + 'my-input': { + template: '' + } + } + }).$mount() + expect(vm.text).toBe('foo') + vm.$refs.input.$emit('input', 'bar') + expect(vm.text).toBe('foo') + vm.$refs.input.$emit('change', 'baz') + expect(vm.text).toBe('baz') + }) + // #8436 it('should not double transform mode props', () => { const BaseInput = { diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index d7faa204944..ca18ce7fb41 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -168,7 +168,7 @@ describe('codegen', () => { it('generate multiline v-model directive on custom component', () => { assertCodegen( '', - `with(this){return _c('my-component',{model:{value:(\n test \n),callback:function ($$v) {\n test \n=$$v},expression:"\\n test \\n"}})}` + `with(this){return _c('my-component',{model:{value:(\n test \n),callback:function ($$v) {\n test \n=$$v},expression:"\\n test \\n",event:'input'}})}` ) }) diff --git a/test/weex/compiler/v-model.spec.js b/test/weex/compiler/v-model.spec.js index c5f0401dc7b..d6f26a49aed 100644 --- a/test/weex/compiler/v-model.spec.js +++ b/test/weex/compiler/v-model.spec.js @@ -14,7 +14,7 @@ describe('compile v-model', () => { it('should compile other component with whole $event as the value', () => { const { render, staticRenderFns, errors } = compile(`
`) expect(render).not.toBeUndefined() - expect(render).toMatch(strToRegExp(`model:{value:(x),callback:function ($$v) {x=$$v},expression:"x"}`)) + expect(render).toMatch(strToRegExp(`model:{value:(x),callback:function ($$v) {x=$$v},expression:"x",event:'input'}`)) expect(staticRenderFns).toEqual([]) expect(errors).toEqual([]) }) From a55adef94ad7b676e241c70d53f56ff329ec13fc Mon Sep 17 00:00:00 2001 From: javoski Date: Fri, 18 Dec 2020 12:46:28 +0800 Subject: [PATCH 2/2] feat(v-model): support customized model option for lazy modifier on components --- flow/compiler.js | 2 +- flow/vnode.js | 2 +- src/compiler/codegen/index.js | 11 +++++-- src/compiler/directives/model.js | 2 +- src/core/vdom/create-component.js | 4 ++- .../directives/model-component.spec.js | 33 ++++++++++++++----- test/unit/modules/compiler/codegen.spec.js | 8 ++++- test/weex/compiler/v-model.spec.js | 2 +- types/options.d.ts | 2 ++ 9 files changed, 49 insertions(+), 17 deletions(-) diff --git a/flow/compiler.js b/flow/compiler.js index 6cd9d3da177..d8decc7b5df 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -163,7 +163,7 @@ declare type ASTElement = { value: string; callback: string; expression: string; - event: string; + lazy: boolean; }; directives?: Array; diff --git a/flow/vnode.js b/flow/vnode.js index 5a07b1fdf37..4ecf686b8c1 100644 --- a/flow/vnode.js +++ b/flow/vnode.js @@ -62,7 +62,7 @@ declare interface VNodeData { model?: { value: any; callback: Function; - event: string; + lazy: boolean; }; }; diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index 9ffda33b084..c95d896d96b 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -279,10 +279,15 @@ export function genData (el: ASTElement, state: CodegenState): string { el.model.callback },expression:${ el.model.expression - },event:${ - el.model.event - }},` + }` + + if (el.model.lazy) { + data += `,lazy:true` + } + + data += '}' } + // inline-template if (el.inlineTemplate) { const inlineTemplate = genInlineTemplate(el, state) diff --git a/src/compiler/directives/model.js b/src/compiler/directives/model.js index 13dd01690eb..eb6c106b2b4 100644 --- a/src/compiler/directives/model.js +++ b/src/compiler/directives/model.js @@ -26,7 +26,7 @@ export function genComponentModel ( el.model = { value: `(${value})`, expression: JSON.stringify(value), - event: lazy ? `'change'` : `'input'`, + lazy: !!lazy, callback: `function (${baseValueExpression}) {${assignment}}` } } diff --git a/src/core/vdom/create-component.js b/src/core/vdom/create-component.js index 910f0050825..633d0b078e8 100644 --- a/src/core/vdom/create-component.js +++ b/src/core/vdom/create-component.js @@ -251,7 +251,9 @@ function mergeHook (f1: any, f2: any): Function { // prop and event handler respectively. function transformModel (options, data: any) { const prop = (options.model && options.model.prop) || 'value' - const event = (options.model && options.model.event) || data.model.event + const defaultEvent = data.model.lazy ? 'change' : 'input' + const event = (options.model && (data.model.lazy ? + options.model.eventLazy : options.model.event)) || defaultEvent ;(data.attrs || (data.attrs = {}))[prop] = data.model.value const on = data.on || (data.on = {}) const existing = on[event] diff --git a/test/unit/features/directives/model-component.spec.js b/test/unit/features/directives/model-component.spec.js index 3ae4627bcaa..7e819f21d24 100644 --- a/test/unit/features/directives/model-component.spec.js +++ b/test/unit/features/directives/model-component.spec.js @@ -151,19 +151,36 @@ describe('Directive v-model component', () => { it('modifier: .lazy', () => { const vm = new Vue({ - template: `
`, - data: { text: 'foo' }, + template: ` +
+ + +
+ `, + data: { text1: 'foo', text2: 'foo' }, components: { - 'my-input': { + 'default-model': { template: '' + }, + 'custom-model': { + template: '', + model: { + eventLazy: 'lazy-update' + } } } }).$mount() - expect(vm.text).toBe('foo') - vm.$refs.input.$emit('input', 'bar') - expect(vm.text).toBe('foo') - vm.$refs.input.$emit('change', 'baz') - expect(vm.text).toBe('baz') + expect(vm.text1).toBe('foo') + vm.$refs.default.$emit('input', 'bar') + expect(vm.text1).toBe('foo') + vm.$refs.default.$emit('change', 'baz') + expect(vm.text1).toBe('baz') + + // customized model option + vm.$refs.custom.$emit('change', 'bar') + expect(vm.text2).toBe('foo') + vm.$refs.custom.$emit('lazy-update', 'baz') + expect(vm.text2).toBe('baz') }) // #8436 diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index ca18ce7fb41..35ca76acd27 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -168,7 +168,13 @@ describe('codegen', () => { it('generate multiline v-model directive on custom component', () => { assertCodegen( '', - `with(this){return _c('my-component',{model:{value:(\n test \n),callback:function ($$v) {\n test \n=$$v},expression:"\\n test \\n",event:'input'}})}` + `with(this){return _c('my-component',{model:{value:(\n test \n),callback:function ($$v) {\n test \n=$$v},expression:"\\n test \\n"}})}` + ) + + // for lazy modifier + assertCodegen( + '', + `with(this){return _c('my-component',{model:{value:(\n test \n),callback:function ($$v) {\n test \n=$$v},expression:"\\n test \\n",lazy:true}})}` ) }) diff --git a/test/weex/compiler/v-model.spec.js b/test/weex/compiler/v-model.spec.js index d6f26a49aed..c5f0401dc7b 100644 --- a/test/weex/compiler/v-model.spec.js +++ b/test/weex/compiler/v-model.spec.js @@ -14,7 +14,7 @@ describe('compile v-model', () => { it('should compile other component with whole $event as the value', () => { const { render, staticRenderFns, errors } = compile(`
`) expect(render).not.toBeUndefined() - expect(render).toMatch(strToRegExp(`model:{value:(x),callback:function ($$v) {x=$$v},expression:"x",event:'input'}`)) + expect(render).toMatch(strToRegExp(`model:{value:(x),callback:function ($$v) {x=$$v},expression:"x"}`)) expect(staticRenderFns).toEqual([]) expect(errors).toEqual([]) }) diff --git a/types/options.d.ts b/types/options.d.ts index 323c7630986..c499d406b9c 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -109,6 +109,7 @@ export interface ComponentOptions< model?: { prop?: string; event?: string; + eventLazy?: string; }; parent?: Vue; @@ -127,6 +128,7 @@ export interface FunctionalComponentOptions