From 154e17abaed2f9c2d80507c28a474adea72337e6 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 21 Jul 2016 01:53:30 -0400 Subject: [PATCH] support binding DOM properties with .prop modifier and :: shorthand --- src/compiler/directives/bind.js | 4 ++- src/compiler/parser/index.js | 11 +++++-- src/core/instance/render.js | 7 +++-- test/unit/features/directives/bind.spec.js | 36 ++++++++++++++++++++++ 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/compiler/directives/bind.js b/src/compiler/directives/bind.js index c5bc9ad4..963206c0 100644 --- a/src/compiler/directives/bind.js +++ b/src/compiler/directives/bind.js @@ -3,5 +3,7 @@ import { addHook } from '../helpers' export default function bind (el: ASTElement, dir: ASTDirective) { - addHook(el, 'construct', `_b(n1,${dir.value})`) + addHook(el, 'construct', `_b(n1,${dir.value}${ + dir.modifiers && dir.modifiers.prop ? ',true' : '' + })`) } diff --git a/src/compiler/parser/index.js b/src/compiler/parser/index.js index 0a11a043..34a2dd61 100644 --- a/src/compiler/parser/index.js +++ b/src/compiler/parser/index.js @@ -3,7 +3,7 @@ import { decodeHTML } from 'entities' import { parseHTML } from './html-parser' import { parseText } from './text-parser' -import { cached, no } from 'shared/util' +import { cached, no, camelize } from 'shared/util' import { pluckModuleFunction, getAndRemoveAttr, @@ -324,7 +324,7 @@ function processComponent (el) { function processAttrs (el) { const list = el.attrsList - let i, l, name, value, arg, modifiers + let i, l, name, value, arg, modifiers, isProp for (i = 0, l = list.length; i < l; i++) { name = list[i].name value = list[i].value @@ -336,7 +336,12 @@ function processAttrs (el) { } if (bindRE.test(name)) { // v-bind name = name.replace(bindRE, '') - if (platformMustUseProp(name)) { + if (name.charAt(0) === ':' || (modifiers && modifiers.prop)) { + isProp = true + name = camelize(name.replace(bindRE, '')) + if (name === 'innerHtml') name = 'innerHTML' + } + if (isProp || platformMustUseProp(name)) { addProp(el, name, value) } else { addAttr(el, name, value) diff --git a/src/core/instance/render.js b/src/core/instance/render.js index dc505808..fc1f8ce9 100644 --- a/src/core/instance/render.js +++ b/src/core/instance/render.js @@ -148,7 +148,10 @@ export function renderMixin (Vue: Class) { } // apply v-bind object - Vue.prototype._b = function bindProps (vnode: VNodeWithData, value: any) { + Vue.prototype._b = function bindProps ( + vnode: VNodeWithData, + value: any, + asProp?: boolean) { if (value) { if (!isObject(value)) { process.env.NODE_ENV !== 'production' && warn( @@ -161,7 +164,7 @@ export function renderMixin (Vue: Class) { } const data = vnode.data for (const key in value) { - const hash = config.mustUseProp(key) + const hash = asProp || config.mustUseProp(key) ? data.domProps || (data.domProps = {}) : data.attrs || (data.attrs = {}) hash[key] = value[key] diff --git a/test/unit/features/directives/bind.spec.js b/test/unit/features/directives/bind.spec.js index d20be03c..2171b7af 100644 --- a/test/unit/features/directives/bind.spec.js +++ b/test/unit/features/directives/bind.spec.js @@ -109,6 +109,18 @@ describe('Directive v-bind', () => { }).then(done) }) + it('bind as prop', () => { + const vm = new Vue({ + template: '
', + data: { + foo: 'hello', + bar: 'qux' + } + }).$mount() + expect(vm.$el.children[0].textContent).toBe('hello') + expect(vm.$el.children[1].innerHTML).toBe('qux') + }) + it('bind object', done => { const vm = new Vue({ template: '', @@ -132,6 +144,30 @@ describe('Directive v-bind', () => { }).then(done) }) + it('bind object as prop', done => { + const vm = new Vue({ + template: '', + data: { + test: { + id: 'test', + className: 'ok', + value: 'hello' + } + } + }).$mount() + expect(vm.$el.id).toBe('test') + expect(vm.$el.className).toBe('ok') + expect(vm.$el.value).toBe('hello') + vm.test.id = 'hi' + vm.test.className = 'okay' + vm.test.value = 'bye' + waitForUpdate(() => { + expect(vm.$el.id).toBe('hi') + expect(vm.$el.className).toBe('okay') + expect(vm.$el.value).toBe('bye') + }).then(done) + }) + it('bind array', done => { const vm = new Vue({ template: '',