var _ = require('../../util') var Watcher = require('../../watcher') module.exports = { bind: function () { var self = this var el = this.el // check options param var optionsParam = this._checkParam('options') if (optionsParam) { initOptions.call(this, optionsParam) } this.multiple = el.hasAttribute('multiple') this.listener = function () { var value = self.multiple ? getMultiValue(el) : el.value self.set(value, true) } _.on(el, 'change', this.listener) checkInitialValue.call(this) }, update: function (value) { /* jshint eqeqeq: false */ var el = this.el el.selectedIndex = -1 var multi = this.multiple && _.isArray(value) var options = el.options var i = options.length var option while (i--) { option = options[i] option.selected = multi ? indexOf(value, option.value) > -1 : value == option.value } }, unbind: function () { _.off(this.el, 'change', this.listener) if (this.optionWatcher) { this.optionWatcher.teardown() } } } /** * Initialize the option list from the param. * * @param {String} expression */ function initOptions (expression) { var self = this function optionUpdateWatcher (value) { if (_.isArray(value)) { self.el.innerHTML = '' buildOptions(self.el, value) if (self._watcher) { self.update(self._watcher.value) } } else { _.warn('Invalid options value for v-model: ' + value) } } this.optionWatcher = new Watcher( this.vm, expression, optionUpdateWatcher ) // update with initial value optionUpdateWatcher(this.optionWatcher.value) } /** * Build up option elements. IE9 doesn't create options * when setting innerHTML on or an * @param {Array} options */ function buildOptions (parent, options) { var op, el for (var i = 0, l = options.length; i < l; i++) { op = options[i] if (!op.options) { el = document.createElement('option') if (typeof op === 'string') { el.text = el.value = op } else { el.text = op.text el.value = op.value } } else { el = document.createElement('optgroup') el.label = op.label buildOptions(el, op.options) } parent.appendChild(el) } } /** * 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 (initValue) { this._initValue = initValue } } /** * Helper to extract a value array for select[multiple] * * @param {SelectElement} el * @return {Array} */ function getMultiValue (el) { return Array.prototype.filter .call(el.options, filterSelected) .map(getOptionValue) } function filterSelected (op) { return op.selected } function getOptionValue (op) { return op.value || op.text } /** * Native Array.indexOf uses strict equal, but in this * case we need to match string/numbers with soft equal. * * @param {Array} arr * @param {*} val */ function indexOf (arr, val) { /* jshint eqeqeq: false */ var i = arr.length while (i--) { if (arr[i] == val) return i } return -1 }