Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/directives/public/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/
// binding v-model to object values
const modelProps = {
value: '_value',
sync: '_sync',
'true-value': '_trueValue',
'false-value': '_falseValue'
}
Expand Down
3 changes: 3 additions & 0 deletions src/directives/public/model/checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export default {
}

this.listener = function () {
if (!self.isSync()) {
return
}
var model = self._watcher.value
if (isArray(model)) {
var val = self.getValue()
Expand Down
7 changes: 6 additions & 1 deletion src/directives/public/model/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { warn, resolveAsset } from '../../../util/index'
import { warn, resolveAsset, toBoolean } from '../../../util/index'
import { MODEL } from '../../priorities'
import text from './text'
import radio from './radio'
Expand Down Expand Up @@ -82,6 +82,11 @@ export default {
}
},

isSync () {
return this.el.hasOwnProperty('_sync')
? toBoolean(this.el._sync) : true
},

unbind () {
this.el.__v_model = null
this._unbind && this._unbind()
Expand Down
2 changes: 1 addition & 1 deletion src/directives/public/model/radio.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default {
}

this.listener = function () {
self.set(self.getValue())
self.isSync() && self.set(self.getValue())
}
this.on('change', this.listener)

Expand Down
3 changes: 3 additions & 0 deletions src/directives/public/model/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export default {

// attach listener
this.listener = function () {
if (!self.isSync()) {
return
}
var value = getValue(el, multiple)
value = self.params.number
? isArray(value)
Expand Down
3 changes: 3 additions & 0 deletions src/directives/public/model/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export default {

// Now attach the main listener
this.listener = this.rawListener = function () {
if (!self.isSync()) {
return
}
if (composing || !self._bound) {
return
}
Expand Down
235 changes: 235 additions & 0 deletions test/unit/specs/directives/public/model_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -781,4 +781,239 @@ describe('v-model', function () {
done()
})
})

it('should control two-way binding for text', function (done) {
var vm = new Vue({
el: el,
data: {
msg: '',
valid: false
},
template: '<input type="text" v-model="msg" :sync="valid">'
})
_.nextTick(function () {
expect(vm.msg).toBe('')
el.firstChild.value = 'hello'
trigger(el.firstChild, 'input')
_.nextTick(function () {
expect(vm.msg).toBe('')
vm.valid = true // two-way on
el.firstChild.value = 'world'
trigger(el.firstChild, 'input')
_.nextTick(function () {
expect(vm.msg).toBe('world')
vm.valid = true // two-way off
el.firstChild.value = 'hello'
_.nextTick(function () {
expect(vm.msg).toBe('world')
done()
})
})
})
})
})

it('should control two-way binding for checkbox', function (done) {
var vm = new Vue({
el: el,
data: {
ok: false,
valid: false
},
template: '<input type="checkbox" v-model="ok" :sync="valid">'
})
_.nextTick(function () {
expect(vm.ok).toBe(false)
el.firstChild.click()
_.nextTick(function () {
expect(vm.ok).toBe(false)
vm.valid = true // two-way on
_.nextTick(function () {
expect(vm.ok).toBe(true)
el.firstChild.click()
_.nextTick(function () {
expect(vm.ok).toBe(false)
vm.valid = false // two-way off
_.nextTick(function () {
el.firstChild.click()
_.nextTick(function () {
expect(vm.ok).toBe(false)
done()
})
})
})
})
})
})
})

it('should control two-way binding for checkbox multiple', function (done) {
var vm = new Vue({
el: el,
data: {
colors: [],
valid: false
},
template:
'<input type="checkbox" value="red" v-model="colors" :sync="valid">' +
'<input type="checkbox" value="blue" v-model="colors" :sync="valid">' +
'<input type="checkbox" value="green" v-model="colors" :sync="valid">'
})
_.nextTick(function () {
expect(vm.colors.length).toBe(0)
el.childNodes[0].click()
_.nextTick(function () {
expect(vm.colors.length).toBe(0)
vm.valid = true // two-way on
_.nextTick(function () {
el.childNodes[1].click()
el.childNodes[2].click()
_.nextTick(function () {
expect(vm.colors.length).toBe(3)
expect(vm.colors[0]).toBe('red')
expect(vm.colors[1]).toBe('blue')
expect(vm.colors[2]).toBe('green')
vm.valid = false // two-way off
_.nextTick(function () {
el.childNodes[0].click()
_.nextTick(function () {
expect(vm.colors.length).toBe(3)
expect(vm.colors[0]).toBe('red')
expect(vm.colors[1]).toBe('blue')
expect(vm.colors[2]).toBe('green')
done()
})
})
})
})
})
})
})

it('should control two-way binding for radio', function (done) {
var vm = new Vue({
el: el,
data: {
test: '',
valid: false
},
template:
'<input type="radio" value="one" v-model="test" :sync="valid">' +
'<input type="radio" value="two" v-model="test" :sync="valid">'
})
_.nextTick(function () {
expect(vm.test).toBe('')
el.childNodes[0].click()
_.nextTick(function () {
expect(vm.test).toBe('')
vm.valid = true // two-way on
_.nextTick(function () {
el.childNodes[1].click()
_.nextTick(function () {
expect(vm.test).toBe('two')
vm.valid = false // two-way off
_.nextTick(function () {
el.childNodes[0].click()
_.nextTick(function () {
expect(vm.test).toBe('two')
done()
})
})
})
})
})
})
})

it('should control two-way binding for select', function (done) {
var vm = new Vue({
el: el,
data: {
test: '',
valid: false
},
template:
'<select v-model="test" :sync="valid">' +
'<option>a</option>' +
'<option>b</option>' +
'<option>c</option>' +
'</select>'
})
_.nextTick(function () {
expect(vm.test).toBe('')
updateSelect(el.firstChild, 'a')
trigger(el.firstChild, 'change')
_.nextTick(function () {
expect(vm.test).toBe('')
vm.valid = true // two-way on
_.nextTick(function () {
updateSelect(el.firstChild, 'c')
trigger(el.firstChild, 'change')
_.nextTick(function () {
expect(vm.test).toBe('c')
vm.valid = false // two-way off
_.nextTick(function () {
updateSelect(el.firstChild, 'b')
trigger(el.firstChild, 'change')
_.nextTick(function () {
expect(vm.test).toBe('c')
done()
})
})
})
})
})
})
})

it('should control two-way binding for select multiple', function (done) {
var vm = new Vue({
el: el,
data: {
test: ['b'],
valid: false
},
template:
'<select multiple v-model="test" :sync="valid">' +
'<option>a</option>' +
'<option>b</option>' +
'<option>c</option>' +
'</select>'
})
var opts = el.firstChild.options
_.nextTick(function () {
expect(vm.test.length).toBe(1)
expect(vm.test[0]).toBe('b')
opts[0].selected = true
trigger(el.firstChild, 'change')
_.nextTick(function () {
expect(vm.test.length).toBe(1)
expect(vm.test[0]).toBe('b')
vm.valid = true // two-way on
_.nextTick(function () {
opts[2].selected = true
trigger(el.firstChild, 'change')
_.nextTick(function () {
expect(vm.test.length).toBe(3)
expect(vm.test[0]).toBe('a')
expect(vm.test[1]).toBe('b')
expect(vm.test[2]).toBe('c')
vm.valid = false // two-way off
_.nextTick(function () {
opts[1].selected = false
opts[2].selected = false
trigger(el.firstChild, 'change')
_.nextTick(function () {
expect(vm.test.length).toBe(3)
expect(vm.test[0]).toBe('a')
expect(vm.test[1]).toBe('b')
expect(vm.test[2]).toBe('c')
done()
})
})
})
})
})
})
})
})