implement all v-model types

This commit is contained in:
Evan You 2016-04-11 22:15:30 -04:00
parent 6cca5ec8a8
commit 8c00a1d724
5 changed files with 60 additions and 104 deletions

View File

@ -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 {

View File

@ -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 genTextModel (events, value) {
function genDefaultModel (events, value) {
addHandler(events, 'input', `${value}=$event.target.value`)
return `value:${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']
}

View File

@ -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.
*

View File

@ -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
}

View File

@ -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)