Skip to content

Commit e16f910

Browse files
committed
select[multiple] support
1 parent 9c2bb94 commit e16f910

File tree

3 files changed

+93
-23
lines changed

3 files changed

+93
-23
lines changed

src/directives/model.js

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
var utils = require('../utils'),
22
isIE9 = navigator.userAgent.indexOf('MSIE 9.0') > 0
33

4+
/**
5+
* Returns an array of values from a multiple select
6+
*/
7+
function getMultipleSelectOptions (select) {
8+
return Array.prototype.filter
9+
.call(select.options, function (option) {
10+
return option.selected
11+
})
12+
.map(function (option) {
13+
return option.value || option.text
14+
})
15+
}
16+
417
module.exports = {
518

619
bind: function () {
@@ -21,17 +34,22 @@ module.exports = {
2134
: 'input'
2235

2336
// determine the attribute to change when updating
24-
var attr = self.attr = type === 'checkbox'
37+
self.attr = type === 'checkbox'
2538
? 'checked'
2639
: (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA')
2740
? 'value'
2841
: 'innerHTML'
2942

43+
// select[multiple] support
44+
if(tag === 'SELECT' && el.hasAttribute('multiple')) {
45+
this.multi = true
46+
}
47+
3048
var compositionLock = false
31-
this.cLock = function () {
49+
self.cLock = function () {
3250
compositionLock = true
3351
}
34-
this.cUnlock = function () {
52+
self.cUnlock = function () {
3553
compositionLock = false
3654
}
3755
el.addEventListener('compositionstart', this.cLock)
@@ -48,10 +66,10 @@ module.exports = {
4866
// so that after vm.$set changes the input
4967
// value we can put the cursor back at where it is
5068
var cursorPos
51-
try {
52-
cursorPos = el.selectionStart
53-
} catch (e) {}
54-
self.vm.$set(self.key, el[attr])
69+
try { cursorPos = el.selectionStart } catch (e) {}
70+
71+
self._set()
72+
5573
// since updates are async
5674
// we need to reset cursor position async too
5775
utils.nextTick(function () {
@@ -64,7 +82,9 @@ module.exports = {
6482
if (compositionLock) return
6583
// no filters, don't let it trigger update()
6684
self.lock = true
67-
self.vm.$set(self.key, el[attr])
85+
86+
self._set()
87+
6888
utils.nextTick(function () {
6989
self.lock = false
7090
})
@@ -90,29 +110,45 @@ module.exports = {
90110
}
91111
},
92112

113+
_set: function () {
114+
this.vm.$set(
115+
this.key, this.multi
116+
? getMultipleSelectOptions(this.el)
117+
: this.el[this.attr]
118+
)
119+
},
120+
93121
update: function (value) {
94-
if (this.lock) return
95122
/* jshint eqeqeq: false */
96-
var self = this,
97-
el = self.el
123+
if (this.lock) return
124+
var el = this.el
98125
if (el.tagName === 'SELECT') { // select dropdown
99-
// setting <select>'s value in IE9 doesn't work
100-
var o = el.options,
101-
i = o.length,
102-
index = -1
103-
while (i--) {
104-
if (o[i].value == value) {
105-
index = i
106-
break
107-
}
126+
el.selectedIndex = -1
127+
if(this.multi && Array.isArray(value)) {
128+
value.forEach(this.updateSelect, this)
129+
} else {
130+
this.updateSelect(value)
108131
}
109-
o.selectedIndex = index
110132
} else if (el.type === 'radio') { // radio button
111133
el.checked = value == el.value
112134
} else if (el.type === 'checkbox') { // checkbox
113135
el.checked = !!value
114136
} else {
115-
el[self.attr] = utils.toText(value)
137+
el[this.attr] = utils.toText(value)
138+
}
139+
},
140+
141+
updateSelect: function (value) {
142+
/* jshint eqeqeq: false */
143+
// setting <select>'s value in IE9 doesn't work
144+
// we have to manually loop through the options
145+
var options = this.el.options,
146+
i = options.length
147+
while (i--) {
148+
if (options[i].value == value) {
149+
options[i].selected = true
150+
break
151+
}
116152
}
117153
},
118154

test/functional/fixtures/forms.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
<option>b</option>
2121
</select>
2222

23+
<select id="multi" v-model="multipleSelect" name="multi" multiple>
24+
<option>a</option>
25+
<option>b</option>
26+
<option>c</option>
27+
<option>d</option>
28+
</select>
29+
2330
<textarea name="textarea" v-model="textarea"></textarea>
2431

2532
</form>
@@ -29,6 +36,7 @@
2936
<p class="checkbox">{{checked}}</p>
3037
<p class="radio">{{radio}}</p>
3138
<p class="select">{{select}}</p>
39+
<p class="multipleSelect">{{multipleSelect}}</p>
3240
<p class="textarea">{{textarea}}</p>
3341
</div>
3442

@@ -41,6 +49,7 @@
4149
checked: true,
4250
radio: 'b',
4351
select: 'b',
52+
multipleSelect: ['a','c'],
4453
textarea: 'more text'
4554
}
4655
})

test/functional/specs/forms.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
casper.test.begin('Forms', 10, function (test) {
1+
casper.test.begin('Forms', 13, function (test) {
22

33
casper
44
.start('./fixtures/forms.html')
@@ -8,6 +8,18 @@ casper.test.begin('Forms', 10, function (test) {
88
test.assertField('checkbox', true)
99
test.assertField('radio', 'b')
1010
test.assertField('select', 'b')
11+
// multiple select's value only returns first selected option
12+
test.assertField('multi', 'a')
13+
// manually retrieve value
14+
test.assertEvalEquals(function () {
15+
var s = document.querySelector('[name="multi"]'),
16+
opts = s.options
17+
return Array.prototype.filter.call(opts, function (o) {
18+
return o.selected
19+
}).map(function (o) {
20+
return o.value || o.text
21+
})
22+
}, ['a', 'c'])
1123
test.assertField('textarea', 'more text')
1224
})
1325
.then(function () {
@@ -18,12 +30,25 @@ casper.test.begin('Forms', 10, function (test) {
1830
'select': 'a',
1931
'textarea': 'more changed text'
2032
})
33+
// Sadly casper doesn't have modifier for clicks
34+
// manually select values and emit a change event...
35+
this.evaluate(function () {
36+
var s = document.querySelector('[name="multi"]'),
37+
o = s.options
38+
s.selectedIndex = -1
39+
o[1].selected = true
40+
o[3].selected = true
41+
var e = document.createEvent('HTMLEvents')
42+
e.initEvent('change', true, true)
43+
s.dispatchEvent(e)
44+
})
2145
})
2246
.then(function () {
2347
test.assertSelectorHasText('.text', 'changed text')
2448
test.assertSelectorHasText('.checkbox', 'false')
2549
test.assertSelectorHasText('.radio', 'a')
2650
test.assertSelectorHasText('.select', 'a')
51+
test.assertSelectorHasText('.multipleSelect', '["b","d"]')
2752
test.assertSelectorHasText('.textarea', 'more changed text')
2853
})
2954
.run(function () {

0 commit comments

Comments
 (0)