From 3965e5053a7d2f22e90f81d4a153d65c1c670897 Mon Sep 17 00:00:00 2001 From: "wenlu.wang" <805037171@163.com> Date: Fri, 30 Jun 2017 08:46:54 +0800 Subject: [PATCH] feat: support sync modifier for v-bind="object" (#5943) close #5937 --- flow/component.js | 2 +- src/compiler/directives/bind.js | 6 ++-- .../render-helpers/bind-object-props.js | 10 ++++++- test/unit/features/directives/bind.spec.js | 30 +++++++++++++++++++ test/unit/modules/compiler/codegen.spec.js | 16 +++++++++- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/flow/component.js b/flow/component.js index 4c9cb0947a..bf2dcb25f7 100644 --- a/flow/component.js +++ b/flow/component.js @@ -120,7 +120,7 @@ declare interface Component { // renderSlot _t: (name: string, fallback: ?Array, props: ?Object) => ?Array; // apply v-bind object - _b: (data: any, value: any, asProp?: boolean) => VNodeData; + _b: (data: any, tag: string, value: any, asProp: boolean, isSync?: boolean) => VNodeData; // check custom keyCode _k: (eventKeyCode: number, key: string, builtInAlias: number | Array | void) => boolean; // resolve scoped slots diff --git a/src/compiler/directives/bind.js b/src/compiler/directives/bind.js index 9f640eeef7..e103572729 100644 --- a/src/compiler/directives/bind.js +++ b/src/compiler/directives/bind.js @@ -2,8 +2,10 @@ export default function bind (el: ASTElement, dir: ASTDirective) { el.wrapData = (code: string) => { - return `_b(${code},'${el.tag}',${dir.value}${ - dir.modifiers && dir.modifiers.prop ? ',true' : '' + return `_b(${code},'${el.tag}',${dir.value},${ + dir.modifiers && dir.modifiers.prop ? 'true' : 'false' + }${ + dir.modifiers && dir.modifiers.sync ? ',true' : '' })` } } diff --git a/src/core/instance/render-helpers/bind-object-props.js b/src/core/instance/render-helpers/bind-object-props.js index cc50c2d9a4..c71dce5c4c 100644 --- a/src/core/instance/render-helpers/bind-object-props.js +++ b/src/core/instance/render-helpers/bind-object-props.js @@ -16,7 +16,8 @@ export function bindObjectProps ( data: any, tag: string, value: any, - asProp?: boolean + asProp: boolean, + isSync?: boolean ): VNodeData { if (value) { if (!isObject(value)) { @@ -44,6 +45,13 @@ export function bindObjectProps ( } if (!(key in hash)) { hash[key] = value[key] + + if (isSync) { + const on = data.on || (data.on = {}) + on[`update:${key}`] = function ($event) { + value[key] = $event + } + } } } } diff --git a/test/unit/features/directives/bind.spec.js b/test/unit/features/directives/bind.spec.js index 3fa905ed25..e9ab1bf736 100644 --- a/test/unit/features/directives/bind.spec.js +++ b/test/unit/features/directives/bind.spec.js @@ -187,6 +187,36 @@ describe('Directive v-bind', () => { }).then(done) }) + it('.sync modifier with bind object', done => { + const vm = new Vue({ + template: ``, + data: { + test: { + fooBar: 1 + } + }, + components: { + test: { + props: ['fooBar'], + template: `
{{ fooBar }}
`, + methods: { + handleUpdate () { + this.$emit('update:fooBar', 2) + } + } + } + } + }).$mount() + expect(vm.$el.textContent).toBe('1') + triggerEvent(vm.$el, 'click') + waitForUpdate(() => { + expect(vm.$el.textContent).toBe('2') + vm.test.fooBar = 3 + }).then(() => { + expect(vm.$el.textContent).toBe('3') + }).then(done) + }) + it('bind object with overwrite', done => { const vm = new Vue({ template: '', diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index 8138164146..097d328dbf 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -126,7 +126,21 @@ describe('codegen', () => { it('generate v-bind directive', () => { assertCodegen( '

', - `with(this){return _c('p',_b({},'p',test))}` + `with(this){return _c('p',_b({},'p',test,false))}` + ) + }) + + it('generate v-bind with prop directive', () => { + assertCodegen( + '

', + `with(this){return _c('p',_b({},'p',test,true))}` + ) + }) + + it('generate v-bind directive with sync modifier', () => { + assertCodegen( + '

', + `with(this){return _c('p',_b({},'p',test,false,true))}` ) })