Explorar o código

support directly use v-for for select options

Evan You %!s(int64=10) %!d(string=hai) anos
pai
achega
afa4d7f01c

+ 9 - 1
src/deprecations.js

@@ -153,7 +153,7 @@ if (process.env.NODE_ENV !== 'production') {
 
     BIND_IS: function () {
       warn(
-        '<component is="{{view}}"> syntax will be depreacted in 1.0.0. Use ' +
+        '<component is="{{view}}"> syntax will be deprecated in 1.0.0. Use ' +
         '<component bind-is="view"> instead.'
       )
     },
@@ -193,6 +193,14 @@ if (process.env.NODE_ENV !== 'production') {
         'Params "exp", "true-exp" and "false-exp" for v-model will be deprecated in 1.0.0. ' +
         'Use "bind-value", "bind-true-value" and "bind-false-value" instead.'
       )
+    },
+
+    SELECT_OPTIONS: function () {
+      warn(
+        'The "options" param for <select v-model> will be deprecated in 1.0.0. ' +
+        'Use v-for to render the options. See https://github.com/yyx990803/vue/issues/1229 ' +
+        'for more details.'
+      )
     }
 
   }

+ 29 - 18
src/directives/for.js

@@ -10,21 +10,6 @@ module.exports = {
   priority: 2000,
 
   bind: function () {
-
-    // some helpful tips...
-    /* istanbul ignore if */
-    if (
-      process.env.NODE_ENV !== 'production' &&
-      this.el.tagName === 'OPTION' &&
-      this.el.parentNode && this.el.parentNode.__v_model
-    ) {
-      _.warn(
-        'Don\'t use v-for for v-model options; ' +
-        'use the `options` param instead: ' +
-        'http://vuejs.org/guide/forms.html#Dynamic_Select_Options'
-      )
-    }
-
     // determine alias
     this.alias = this.arg
     // support "item in items" syntax
@@ -44,6 +29,17 @@ module.exports = {
     // uid as a cache identifier
     this.id = '__v-for__' + (++uid)
 
+    // check if this is an option list,
+    // so that we know if we need to update the <select>'s
+    // v-model when the option list has changed.
+    // because v-model has a lower priority than v-for,
+    // the v-model is not bound here yet, so we have to
+    // retrive it in the actual updateModel() function.
+    var tag = this.el.tagName
+    this.isOption =
+      (tag === 'OPTION' || tag === 'OPTGROUP') &&
+      this.el.parentNode.tagName === 'SELECT'
+
     // setup anchor nodes
     this.start = _.createAnchor('v-for-start')
     this.end = _.createAnchor('v-for-end')
@@ -76,9 +72,8 @@ module.exports = {
 
   update: function (data) {
     this.diff(data)
-    if (this.ref) {
-      this.updateRef()
-    }
+    this.updateRef()
+    this.updateModel()
   },
 
   /**
@@ -235,6 +230,7 @@ module.exports = {
 
   updateRef: function () {
     var ref = this.ref
+    if (!ref) return
     var hash = (this._scope || this.vm).$
     var refs
     if (!this.converted) {
@@ -252,6 +248,21 @@ module.exports = {
     }
   },
 
+  /**
+   * For option lists, update the containing v-model on
+   * parent <select>.
+   */
+
+  updateModel: function () {
+    if (this.isOption) {
+      var parent = this.start.parentNode
+      var model = parent && parent.__v_model
+      if (model) {
+        model.forceUpdate()
+      }
+    }
+  },
+
   /**
    * Insert a fragment. Handles staggering.
    *

+ 3 - 0
src/directives/model/select.js

@@ -19,6 +19,9 @@ module.exports = {
     var optionsParam = this.param('options')
     if (optionsParam) {
       initOptions.call(this, optionsParam)
+      if (process.env.NODE_ENV !== 'production') {
+        _.deprecation.SELECT_OPTIONS()
+      }
     }
     this.number = this.param('number') != null
     this.multiple = el.hasAttribute('multiple')

+ 0 - 2
src/directives/repeat.js

@@ -17,8 +17,6 @@ var ABORTED = 3
 
 module.exports = {
 
-  priority: 2000,
-
   /**
    * Setup.
    */

+ 44 - 0
test/unit/specs/directives/model_spec.js

@@ -576,6 +576,50 @@ if (_.inBrowser) {
       })
     })
 
+    it('select + v-for (1.0.0)', function (done) {
+      var vm = new Vue({
+        el: el,
+        data: {
+          test: { msg: 'A' },
+          opts: [
+            { text: 'a', value: { msg: 'A' }},
+            { text: 'b', value: { msg: 'B' }}
+          ]
+        },
+        template:
+          '<select v-model="test">' +
+            '<option v-for="op in opts" bind-value="op.value">{{op.text}}</option>' +
+          '</select>'
+      })
+      var select = el.firstChild
+      var opts = select.options
+      expect(opts[0].selected).toBe(true)
+      expect(opts[1].selected).toBe(false)
+      expect(vm.test.msg).toBe('A')
+      opts[1].selected = true
+      trigger(select, 'change')
+      _.nextTick(function () {
+        expect(opts[0].selected).toBe(false)
+        expect(opts[1].selected).toBe(true)
+        expect(vm.test.msg).toBe('B')
+        vm.test = { msg: 'A' }
+        _.nextTick(function () {
+          expect(opts[0].selected).toBe(true)
+          expect(opts[1].selected).toBe(false)
+          vm.test = { msg: 'C' }
+          vm.opts.push({text: 'c', value: vm.test})
+          _.nextTick(function () {
+            // updating the opts array should force the
+            // v-model to update
+            expect(opts[0].selected).toBe(false)
+            expect(opts[1].selected).toBe(false)
+            expect(opts[2].selected).toBe(true)
+            done()
+          })
+        })
+      })
+    })
+
     it('text', function (done) {
       var vm = new Vue({
         el: el,