Jelajahi Sumber

v-model: checkbox support array model + use afterBind for initializing inline value

Evan You 10 tahun lalu
induk
melakukan
22c8a494a1

+ 5 - 2
src/directive.js

@@ -116,8 +116,11 @@ Directive.prototype._bind = function () {
         scope: this._scope
       }
     )
-    if (this._initValue != null) {
-      watcher.set(this._initValue)
+    // v-model with inital inline value need to sync back to
+    // model instead of update to DOM on init. They would
+    // set the afterBind hook to indicate that.
+    if (this.afterBind) {
+      this.afterBind()
     } else if (this.update) {
       this.update(watcher.value)
     }

+ 34 - 14
src/directives/public/model/checkbox.js

@@ -6,15 +6,7 @@ module.exports = {
     var self = this
     var el = this.el
 
-    this._matchValue = function (value) {
-      if (el.hasOwnProperty('_trueValue')) {
-        return _.looseEqual(value, el._trueValue)
-      } else {
-        return !!value
-      }
-    }
-
-    function getValue () {
+    function getBooleanValue () {
       var val = el.checked
       if (val && el.hasOwnProperty('_trueValue')) {
         return el._trueValue
@@ -25,16 +17,44 @@ module.exports = {
       return val
     }
 
-    this.on('change', function () {
-      self.set(getValue())
-    })
+    this.listener = function () {
+      var model = self._watcher.value
+      if (_.isArray(model)) {
+        var val = getValue(el)
+        if (el.checked) {
+          if (_.indexOf(model, val) < 0) {
+            model.push(val)
+          }
+        } else {
+          model.$remove(val)
+        }
+      } else {
+        self.set(getBooleanValue())
+      }
+    }
 
+    this.on('change', this.listener)
     if (el.checked) {
-      this._initValue = getValue()
+      this.afterBind = this.listener
     }
   },
 
   update: function (value) {
-    this.el.checked = this._matchValue(value)
+    var el = this.el
+    if (_.isArray(value)) {
+      el.checked = _.indexOf(value, getValue(el)) > -1
+    } else {
+      if (el.hasOwnProperty('_trueValue')) {
+        el.checked = _.looseEqual(value, el._trueValue)
+      } else {
+        el.checked = !!value
+      }
+    }
   }
 }
+
+function getValue (el) {
+  return el.hasOwnProperty('_value')
+    ? el._value
+    : el.value
+}

+ 4 - 3
src/directives/public/model/radio.js

@@ -19,12 +19,13 @@ module.exports = {
       return val
     }
 
-    this.on('change', function () {
+    this.listener = function () {
       self.set(self.getValue())
-    })
+    }
+    this.on('change', this.listener)
 
     if (el.checked) {
-      this._initValue = this.getValue()
+      this.afterBind = this.listener
     }
   },
 

+ 18 - 33
src/directives/public/model/select.js

@@ -14,21 +14,26 @@ module.exports = {
     }
 
     this.number = this.param('number') != null
-    this.multiple = el.hasAttribute('multiple')
+    var multiple = this.multiple = el.hasAttribute('multiple')
 
     // attach listener
-    this.on('change', function () {
-      var value = getValue(el, self.multiple)
+    this.listener = function () {
+      var value = getValue(el, multiple)
       value = self.number
         ? _.isArray(value)
           ? value.map(_.toNumber)
           : _.toNumber(value)
         : value
       self.set(value)
-    })
+    }
+    this.on('change', this.listener)
 
-    // check initial value (inline selected attribute)
-    checkInitialValue.call(this)
+    // if has initial value, set afterBind
+    var initValue = getValue(el, multiple, true)
+    if ((multiple && initValue.length) ||
+        (!multiple && initValue !== null)) {
+      this.afterBind = this.listener
+    }
 
     // All major browsers except Firefox resets
     // selectedIndex with value -1 to 0 when the element
@@ -63,44 +68,24 @@ module.exports = {
   }
 }
 
-/**
- * Check the initial value for selected options.
- */
-
-function checkInitialValue () {
-  var initValue
-  var options = this.el.options
-  for (var i = 0, l = options.length; i < l; i++) {
-    if (options[i].hasAttribute('selected')) {
-      if (this.multiple) {
-        (initValue || (initValue = []))
-          .push(options[i].value)
-      } else {
-        initValue = options[i].value
-      }
-    }
-  }
-  if (typeof initValue !== 'undefined') {
-    this._initValue = this.number
-      ? _.toNumber(initValue)
-      : initValue
-  }
-}
-
 /**
  * Get select value
  *
  * @param {SelectElement} el
  * @param {Boolean} multi
+ * @param {Boolean} init
  * @return {Array|*}
  */
 
-function getValue (el, multi) {
+function getValue (el, multi, init) {
   var res = multi ? [] : null
-  var op, val
+  var op, val, selected
   for (var i = 0, l = el.options.length; i < l; i++) {
     op = el.options[i]
-    if (op.selected) {
+    selected = init
+      ? op.hasAttribute('selected')
+      : op.selected
+    if (selected) {
       val = op.hasOwnProperty('_value')
         ? op._value
         : op.value

+ 1 - 3
src/directives/public/model/text.js

@@ -112,9 +112,7 @@ module.exports = {
       el.hasAttribute('value') ||
       (el.tagName === 'TEXTAREA' && el.value.trim())
     ) {
-      this._initValue = number
-        ? _.toNumber(el.value)
-        : el.value
+      this.afterBind = this.listener
     }
   },