Evan You 10 роки тому
батько
коміт
c2ef6d8df9

+ 22 - 13
src/platforms/web/compiler/directives/model.js

@@ -18,7 +18,7 @@ export default function model (
   } else {
     switch (el.attrsMap.type) {
       case 'checkbox':
-        genCheckboxModel(el, value)
+        genCheckboxModel(el, value, warn)
         break
       case 'radio':
         genRadioModel(el, value, warn)
@@ -29,17 +29,27 @@ export default function model (
   }
 }
 
-function genCheckboxModel (el: ASTElement, value: ?string) {
+function genCheckboxModel (el: ASTElement, value: ?string, warn: Function) {
+  if (process.env.NODE_ENV !== 'production' &&
+    el.attrsMap.checked != null) {
+    warn(
+      `<${el.tag} v-model="${value}" checked>:\n` +
+      `inline checked attributes will be ignored when using v-model. ` +
+      'Declare initial values in the component\'s data option instead.'
+    )
+  }
   const valueBinding = getBindingAttr(el, 'value')
+  const trueValueBinding = getBindingAttr(el, 'true-value') || 'true'
+  const falseValueBinding = getBindingAttr(el, 'false-value') || 'false'
   addProp(el, 'checked',
     `Array.isArray(${value})` +
       `?(${value}).indexOf(${valueBinding})>-1` +
-      `:!!(${value})`
+      `:(${value})==(${trueValueBinding})`
   )
   addHandler(el, 'change',
     `var $$a=${value},` +
         '$$el=$event.target,' +
-        '$$c=$$el.checked;' +
+        `$$c=$$el.checked?(${trueValueBinding}):(${falseValueBinding});` +
     'if(Array.isArray($$a)){' +
       `var $$v=${valueBinding},` +
           '$$i=$$a.indexOf($$v);' +
@@ -50,17 +60,16 @@ function genCheckboxModel (el: ASTElement, value: ?string) {
 }
 
 function genRadioModel (el: ASTElement, value: ?string, warn: Function) {
-  if (process.env.NODE_ENV !== 'production') {
-    if (el.attrsMap.checked != null) {
-      warn(
-        `<${el.tag} v-model="${value}" checked>:\n` +
-        `inline checked attributes will be ignored when using v-model. ` +
-        'Declare initial values in the component\'s data option instead.'
-      )
-    }
+  if (process.env.NODE_ENV !== 'production' &&
+    el.attrsMap.checked != null) {
+    warn(
+      `<${el.tag} v-model="${value}" checked>:\n` +
+      `inline checked attributes will be ignored when using v-model. ` +
+      'Declare initial values in the component\'s data option instead.'
+    )
   }
   const valueBinding = getBindingAttr(el, 'value')
-  addProp(el, 'checked', `(${value}==${valueBinding})`)
+  addProp(el, 'checked', `(${value})==(${valueBinding})`)
   addHandler(el, 'change', `${value}=${valueBinding}`)
 }
 

+ 121 - 0
test/unit/features/directives/model-checkbox.spec.js

@@ -0,0 +1,121 @@
+import Vue from 'vue'
+
+describe('Directive v-model checkbox', () => {
+  it('should work', done => {
+    const vm = new Vue({
+      data: {
+        test: true
+      },
+      template: '<input type="checkbox" v-model="test">'
+    }).$mount()
+    document.body.appendChild(vm.$el)
+    expect(vm.$el.checked).toBe(true)
+    vm.test = false
+    waitForUpdate(function () {
+      expect(vm.$el.checked).toBe(false)
+      expect(vm.test).toBe(false)
+      vm.$el.click()
+      expect(vm.$el.checked).toBe(true)
+      expect(vm.test).toBe(true)
+    }).then(() => {
+      document.body.removeChild(vm.$el)
+    }).then(done)
+  })
+
+  it('should respect value bindings', done => {
+    const vm = new Vue({
+      data: {
+        test: 1,
+        a: 1,
+        b: 2
+      },
+      template: '<input type="checkbox" v-model="test" :true-value="a" :false-value="b">'
+    }).$mount()
+    document.body.appendChild(vm.$el)
+    expect(vm.$el.checked).toBe(true)
+    vm.$el.click()
+    expect(vm.$el.checked).toBe(false)
+    expect(vm.test).toBe(2)
+    vm.$el.click()
+    expect(vm.$el.checked).toBe(true)
+    expect(vm.test).toBe(1)
+    waitForUpdate(() => {
+      vm.test = 2
+    }).then(() => {
+      expect(vm.$el.checked).toBe(false)
+      vm.test = 1
+    }).then(() => {
+      expect(vm.$el.checked).toBe(true)
+      document.body.removeChild(vm.$el)
+    }).then(done)
+  })
+
+  it('bind to Array value', done => {
+    const vm = new Vue({
+      data: {
+        test: ['1']
+      },
+      template: `
+        <div>
+          <input type="checkbox" v-model="test" value="1">
+          <input type="checkbox" v-model="test" value="2">
+        </div>
+      `
+    }).$mount()
+    document.body.appendChild(vm.$el)
+    expect(vm.$el.children[0].checked).toBe(true)
+    expect(vm.$el.children[1].checked).toBe(false)
+    vm.$el.children[0].click()
+    expect(vm.test.length).toBe(0)
+    vm.$el.children[1].click()
+    expect(vm.test).toEqual(['2'])
+    vm.$el.children[0].click()
+    expect(vm.test).toEqual(['2', '1'])
+    waitForUpdate(() => {
+      vm.test = ['1']
+    }).then(() => {
+      expect(vm.$el.children[0].checked).toBe(true)
+      expect(vm.$el.children[1].checked).toBe(false)
+    }).then(done)
+  })
+
+  it('bind to Array value with value bindings', done => {
+    const vm = new Vue({
+      data: {
+        test: [1]
+      },
+      template: `
+        <div>
+          <input type="checkbox" v-model="test" :value="1">
+          <input type="checkbox" v-model="test" :value="2">
+        </div>
+      `
+    }).$mount()
+    document.body.appendChild(vm.$el)
+    expect(vm.$el.children[0].checked).toBe(true)
+    expect(vm.$el.children[1].checked).toBe(false)
+    vm.$el.children[0].click()
+    expect(vm.test.length).toBe(0)
+    vm.$el.children[1].click()
+    expect(vm.test).toEqual([2])
+    vm.$el.children[0].click()
+    expect(vm.test).toEqual([2, 1])
+    waitForUpdate(() => {
+      vm.test = [1]
+    }).then(() => {
+      expect(vm.$el.children[0].checked).toBe(true)
+      expect(vm.$el.children[1].checked).toBe(false)
+    }).then(done)
+  })
+
+  it('warn inline checked', () => {
+    const vm = new Vue({
+      template: `<input type="checkbox" v-model="test" checked>`,
+      data: {
+        test: false
+      }
+    }).$mount()
+    expect(vm.$el.checked).toBe(false)
+    expect('inline checked attributes will be ignored when using v-model').toHaveBeenWarned()
+  })
+})