From 8c00a1d7247aeee00f37c063ff5a1ed40ecc7fe5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 11 Apr 2016 22:15:30 -0400 Subject: [PATCH] implement all v-model types --- src/compiler/codegen/index.js | 10 ++++- src/compiler/codegen/model.js | 63 +++++++++++++++++++-------- src/config.js | 7 --- src/observer/watcher.js | 80 ++--------------------------------- src/vdom/modules/attrs.js | 2 +- 5 files changed, 59 insertions(+), 103 deletions(-) diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index d984d797..c582931b 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -69,6 +69,13 @@ function genData (el, key) { let hasAttrs = false let hasProps = false let hasEvents = false + + // parent elements my need to add props to children + if (el.props) { + hasProps = true + props += el.props + ',' + } + for (let i = 0, l = el.attrs.length; i < l; i++) { let attr = el.attrs[i] let name = attr.name @@ -89,9 +96,8 @@ function genData (el, key) { name = name.replace(onRE, '') addHandler(events, name, value) } else if (name === 'v-model') { - // TODO: handle other input types hasProps = hasEvents = true - props += genModel(el, events, value) + props += genModel(el, events, value) + ',' } else if (dirRE.test(name)) { // TODO: normal directives } else { diff --git a/src/compiler/codegen/model.js b/src/compiler/codegen/model.js index 380cc455..52085390 100644 --- a/src/compiler/codegen/model.js +++ b/src/compiler/codegen/model.js @@ -1,31 +1,60 @@ import { addHandler } from './events' export function genModel (el, events, value) { - switch (el.attrsMap.type) { - case 'checkbox': - return genCheckboxModel(events, value) - case 'radio': - return genRadioModel(events, value) - case 'select': - return genSelectModel(events, value) - default: - return genTextModel(events, value) + if (el.tag === 'select') { + if (el.attrsMap.multiple != null) { + return genMultiSelect(events, value, el) + } else { + return genSelect(events, value) + } + } else { + switch (el.attrsMap.type) { + case 'checkbox': + return genCheckboxModel(events, value) + case 'radio': + return genRadioModel(events, value, el) + default: + return genDefaultModel(events, value) + } } } function genCheckboxModel (events, value) { - // TODO + addHandler(events, 'change', `${value}=$event.target.checked`) + return `checked:!!(${value})` } -function genRadioModel (events, value) { - // TODO +function genRadioModel (events, value, el) { + addHandler(events, 'change', `${value}=$event.target.value`) + return `checked:(${value}==${getInputValue(el)})` } -function genSelectModel (events, value) { - // TODO +function genDefaultModel (events, value) { + addHandler(events, 'input', `${value}=$event.target.value`) + return `value:(${value})` } -function genTextModel (events, value) { - addHandler(events, 'input', `${value}=$event.target.value`) - return `value:${value},` +function genSelect (events, value) { + addHandler(events, 'change', `${value}=$event.target.value`) + return `value:(${value})` +} + +function genMultiSelect (events, value, el) { + addHandler(events, 'change', `${value}=Array.prototype.filter + .call($event.target.options,function(o){return o.selected}) + .map(function(o){return o.value})`) + // patch child options + for (let i = 0; i < el.children.length; i++) { + let c = el.children[i] + if (c.tag === 'option') { + c.props = `selected:(${value}).indexOf(${getInputValue(c)})>-1` + } + } + return '' +} + +function getInputValue (el) { + return el.attrsMap.value + ? JSON.stringify(el.attrsMap.value) + : el.attrsMap['v-bind:value'] || el.attrsMap[':value'] } diff --git a/src/config.js b/src/config.js index 59f0bdca..f77b84f6 100644 --- a/src/config.js +++ b/src/config.js @@ -14,13 +14,6 @@ export default { silent: false, - /** - * Whether to warn against errors caught when evaluating - * expressions. - */ - - warnExpressionErrors: true, - /** * List of asset types that a component can own. * diff --git a/src/observer/watcher.js b/src/observer/watcher.js index 01cb6cf2..5b85e5c5 100644 --- a/src/observer/watcher.js +++ b/src/observer/watcher.js @@ -49,13 +49,11 @@ export default function Watcher (vm, expOrFn, cb, options) { this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() - this.prevError = null // for async error stacks - // parse expression for getter/setter + // parse expression for getter if (isFn) { this.getter = expOrFn - this.setter = undefined } else { - warn('vue-lite only supports watching functions.') + this.getter = new Function(`with(this){return ${expOrFn}}`) } this.value = this.lazy ? undefined @@ -71,68 +69,16 @@ export default function Watcher (vm, expOrFn, cb, options) { Watcher.prototype.get = function () { this.beforeGet() - var scope = this.scope || this.vm - var value - try { - value = this.getter.call(scope, scope) - } catch (e) { - if ( - process.env.NODE_ENV !== 'production' && - config.warnExpressionErrors - ) { - warn( - 'Error when evaluating expression ' + - '"' + this.expression + '": ' + e.toString(), - this.vm - ) - } - } + const value = this.getter.call(this.vm, this.vm) // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value) } - if (this.preProcess) { - value = this.preProcess(value) - } - if (this.filters) { - value = scope._applyFilters(value, null, this.filters, false) - } - if (this.postProcess) { - value = this.postProcess(value) - } this.afterGet() return value } -/** - * Set the corresponding value with the setter. - * - * @param {*} value - */ - -Watcher.prototype.set = function (value) { - var scope = this.scope || this.vm - if (this.filters) { - value = scope._applyFilters( - value, this.value, this.filters, true) - } - try { - this.setter.call(scope, scope, value) - } catch (e) { - if ( - process.env.NODE_ENV !== 'production' && - config.warnExpressionErrors - ) { - warn( - 'Error when evaluating setter ' + - '"' + this.expression + '": ' + e.toString(), - this.vm - ) - } - } -} - /** * Prepare for dependency collection. */ @@ -230,25 +176,7 @@ Watcher.prototype.run = function () { // set new value var oldValue = this.value this.value = value - // in debug + async mode, when a watcher callbacks - // throws, we also throw the saved before-push error - // so the full cross-tick stack trace is available. - var prevError = this.prevError - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && - config.debug && prevError) { - this.prevError = null - try { - this.cb.call(this.vm, value, oldValue) - } catch (e) { - nextTick(function () { - throw prevError - }, 0) - throw e - } - } else { - this.cb.call(this.vm, value, oldValue) - } + this.cb.call(this.vm, value, oldValue) } this.queued = this.shallow = false } diff --git a/src/vdom/modules/attrs.js b/src/vdom/modules/attrs.js index 63ace116..ddc5c424 100644 --- a/src/vdom/modules/attrs.js +++ b/src/vdom/modules/attrs.js @@ -24,7 +24,7 @@ function updateAttrs (oldVnode, vnode) { old = oldAttrs[key] if (old !== cur) { // TODO: add support to namespaced attributes (setAttributeNS) - if(!cur && booleanAttrsDict[key]) + if(booleanAttrsDict[key] && cur == null) elm.removeAttribute(key) else elm.setAttribute(key, cur)