From 7b7164c11cbb74ed44ee086f0a82acfcc1ff47a2 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 29 Nov 2018 18:09:37 -0500 Subject: [PATCH] fix(v-model): avoid duplicate model transforms This happens when a component directly passes down its own data object to a child component. Fix #8436. --- src/core/vdom/create-component.js | 14 ++++- .../directives/model-component.spec.js | 56 +++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/core/vdom/create-component.js b/src/core/vdom/create-component.js index 697afbefb22..b769fa1e19d 100644 --- a/src/core/vdom/create-component.js +++ b/src/core/vdom/create-component.js @@ -252,9 +252,17 @@ function transformModel (options, data: any) { const event = (options.model && options.model.event) || 'input' ;(data.props || (data.props = {}))[prop] = data.model.value const on = data.on || (data.on = {}) - if (isDef(on[event])) { - on[event] = [data.model.callback].concat(on[event]) + const existing = on[event] + const callback = data.model.callback + if (isDef(existing)) { + if ( + Array.isArray(existing) + ? existing.indexOf(callback) === -1 + : existing !== callback + ) { + on[event] = [callback].concat(existing) + } } else { - on[event] = data.model.callback + on[event] = callback } } diff --git a/test/unit/features/directives/model-component.spec.js b/test/unit/features/directives/model-component.spec.js index 6098e4d6241..9f9536ada09 100644 --- a/test/unit/features/directives/model-component.spec.js +++ b/test/unit/features/directives/model-component.spec.js @@ -148,4 +148,60 @@ describe('Directive v-model component', () => { vm.$refs.input.$emit('input', ' foo o ') expect(vm.text).toBe('foo o') }) + + // #8436 + it('should not double transform mode props', () => { + const BaseInput = { + props: ['value'], + render (h) { + return h('input', { + domProps: { + value: this.value + }, + on: { + input: e => this.$emit('input', e.target.value) + } + }) + } + } + + const FunctionalWrapper = { + functional: true, + render (h, ctx) { + return h(BaseInput, ctx.data) + } + } + + let triggerCount = 0 + + const vm = new Vue({ + components: { + FunctionalWrapper + }, + template: ` +
+ +
+ `, + data: { + internalVal: '' + }, + computed: { + val: { + get () { + return this.internalVal + }, + set (val) { + triggerCount++ + this.internalVal = val + } + } + } + }).$mount() + + document.body.appendChild(vm.$el) + triggerEvent(vm.$el.querySelector('input'), 'input') + expect(triggerCount).toBe(1) + document.body.removeChild(vm.$el) + }) })