Skip to content

Commit 622acc6

Browse files
committed
support directly use v-for for select options
1 parent 430c295 commit 622acc6

File tree

5 files changed

+85
-21
lines changed

5 files changed

+85
-21
lines changed

src/deprecations.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ if (process.env.NODE_ENV !== 'production') {
153153

154154
BIND_IS: function () {
155155
warn(
156-
'<component is="{{view}}"> syntax will be depreacted in 1.0.0. Use ' +
156+
'<component is="{{view}}"> syntax will be deprecated in 1.0.0. Use ' +
157157
'<component bind-is="view"> instead.'
158158
)
159159
},
@@ -193,6 +193,14 @@ if (process.env.NODE_ENV !== 'production') {
193193
'Params "exp", "true-exp" and "false-exp" for v-model will be deprecated in 1.0.0. ' +
194194
'Use "bind-value", "bind-true-value" and "bind-false-value" instead.'
195195
)
196+
},
197+
198+
SELECT_OPTIONS: function () {
199+
warn(
200+
'The "options" param for <select v-model> will be deprecated in 1.0.0. ' +
201+
'Use v-for to render the options. See https://github.com/yyx990803/vue/issues/1229 ' +
202+
'for more details.'
203+
)
196204
}
197205

198206
}

src/directives/for.js

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,6 @@ module.exports = {
1010
priority: 2000,
1111

1212
bind: function () {
13-
14-
// some helpful tips...
15-
/* istanbul ignore if */
16-
if (
17-
process.env.NODE_ENV !== 'production' &&
18-
this.el.tagName === 'OPTION' &&
19-
this.el.parentNode && this.el.parentNode.__v_model
20-
) {
21-
_.warn(
22-
'Don\'t use v-for for v-model options; ' +
23-
'use the `options` param instead: ' +
24-
'http://vuejs.org/guide/forms.html#Dynamic_Select_Options'
25-
)
26-
}
27-
2813
// determine alias
2914
this.alias = this.arg
3015
// support "item in items" syntax
@@ -44,6 +29,17 @@ module.exports = {
4429
// uid as a cache identifier
4530
this.id = '__v-for__' + (++uid)
4631

32+
// check if this is an option list,
33+
// so that we know if we need to update the <select>'s
34+
// v-model when the option list has changed.
35+
// because v-model has a lower priority than v-for,
36+
// the v-model is not bound here yet, so we have to
37+
// retrive it in the actual updateModel() function.
38+
var tag = this.el.tagName
39+
this.isOption =
40+
(tag === 'OPTION' || tag === 'OPTGROUP') &&
41+
this.el.parentNode.tagName === 'SELECT'
42+
4743
// setup anchor nodes
4844
this.start = _.createAnchor('v-for-start')
4945
this.end = _.createAnchor('v-for-end')
@@ -76,9 +72,8 @@ module.exports = {
7672

7773
update: function (data) {
7874
this.diff(data)
79-
if (this.ref) {
80-
this.updateRef()
81-
}
75+
this.updateRef()
76+
this.updateModel()
8277
},
8378

8479
/**
@@ -235,6 +230,7 @@ module.exports = {
235230

236231
updateRef: function () {
237232
var ref = this.ref
233+
if (!ref) return
238234
var hash = (this._scope || this.vm).$
239235
var refs
240236
if (!this.converted) {
@@ -252,6 +248,21 @@ module.exports = {
252248
}
253249
},
254250

251+
/**
252+
* For option lists, update the containing v-model on
253+
* parent <select>.
254+
*/
255+
256+
updateModel: function () {
257+
if (this.isOption) {
258+
var parent = this.start.parentNode
259+
var model = parent && parent.__v_model
260+
if (model) {
261+
model.forceUpdate()
262+
}
263+
}
264+
},
265+
255266
/**
256267
* Insert a fragment. Handles staggering.
257268
*

src/directives/model/select.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ module.exports = {
1919
var optionsParam = this.param('options')
2020
if (optionsParam) {
2121
initOptions.call(this, optionsParam)
22+
if (process.env.NODE_ENV !== 'production') {
23+
_.deprecation.SELECT_OPTIONS()
24+
}
2225
}
2326
this.number = this.param('number') != null
2427
this.multiple = el.hasAttribute('multiple')

src/directives/repeat.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ var ABORTED = 3
1717

1818
module.exports = {
1919

20-
priority: 2000,
21-
2220
/**
2321
* Setup.
2422
*/

test/unit/specs/directives/model_spec.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,50 @@ if (_.inBrowser) {
576576
})
577577
})
578578

579+
it('select + v-for (1.0.0)', function (done) {
580+
var vm = new Vue({
581+
el: el,
582+
data: {
583+
test: { msg: 'A' },
584+
opts: [
585+
{ text: 'a', value: { msg: 'A' }},
586+
{ text: 'b', value: { msg: 'B' }}
587+
]
588+
},
589+
template:
590+
'<select v-model="test">' +
591+
'<option v-for="op in opts" bind-value="op.value">{{op.text}}</option>' +
592+
'</select>'
593+
})
594+
var select = el.firstChild
595+
var opts = select.options
596+
expect(opts[0].selected).toBe(true)
597+
expect(opts[1].selected).toBe(false)
598+
expect(vm.test.msg).toBe('A')
599+
opts[1].selected = true
600+
trigger(select, 'change')
601+
_.nextTick(function () {
602+
expect(opts[0].selected).toBe(false)
603+
expect(opts[1].selected).toBe(true)
604+
expect(vm.test.msg).toBe('B')
605+
vm.test = { msg: 'A' }
606+
_.nextTick(function () {
607+
expect(opts[0].selected).toBe(true)
608+
expect(opts[1].selected).toBe(false)
609+
vm.test = { msg: 'C' }
610+
vm.opts.push({text: 'c', value: vm.test})
611+
_.nextTick(function () {
612+
// updating the opts array should force the
613+
// v-model to update
614+
expect(opts[0].selected).toBe(false)
615+
expect(opts[1].selected).toBe(false)
616+
expect(opts[2].selected).toBe(true)
617+
done()
618+
})
619+
})
620+
})
621+
})
622+
579623
it('text', function (done) {
580624
var vm = new Vue({
581625
el: el,

0 commit comments

Comments
 (0)