Răsfoiți Sursa

support binding DOM properties with .prop modifier and :: shorthand

Evan You 9 ani în urmă
părinte
comite
154e17abae

+ 3 - 1
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' : ''
+  })`)
 }

+ 8 - 3
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)

+ 5 - 2
src/core/instance/render.js

@@ -148,7 +148,10 @@ export function renderMixin (Vue: Class<Component>) {
   }
 
   // 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<Component>) {
         }
         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]

+ 36 - 0
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: '<div><span v-bind:text-content.prop="foo"></span><span ::inner-html="bar"></span></div>',
+      data: {
+        foo: 'hello',
+        bar: '<span>qux</span>'
+      }
+    }).$mount()
+    expect(vm.$el.children[0].textContent).toBe('hello')
+    expect(vm.$el.children[1].innerHTML).toBe('<span>qux</span>')
+  })
+
   it('bind object', done => {
     const vm = new Vue({
       template: '<input v-bind="test">',
@@ -132,6 +144,30 @@ describe('Directive v-bind', () => {
     }).then(done)
   })
 
+  it('bind object as prop', done => {
+    const vm = new Vue({
+      template: '<input v-bind.prop="test">',
+      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: '<input v-bind="test">',