|
|
@@ -1,5 +1,6 @@
|
|
|
/* @flow */
|
|
|
|
|
|
+import config from 'core/config'
|
|
|
import { isIE } from 'core/util/env'
|
|
|
import { addHandler, addProp, getBindingAttr, parseModel } from 'compiler/helpers'
|
|
|
|
|
|
@@ -40,8 +41,19 @@ export default function model (
|
|
|
genCheckboxModel(el, value, modifiers)
|
|
|
} else if (tag === 'input' && type === 'radio') {
|
|
|
genRadioModel(el, value, modifiers)
|
|
|
- } else {
|
|
|
+ } else if (tag === 'input' || tag === 'textarea') {
|
|
|
genDefaultModel(el, value, modifiers)
|
|
|
+ } else if (!config.isReservedTag(tag)) {
|
|
|
+ genComponentModel(el, value, modifiers)
|
|
|
+ // component v-model doesn't need extra runtime
|
|
|
+ return false
|
|
|
+ } else if (process.env.NODE_ENV !== 'production') {
|
|
|
+ warn(
|
|
|
+ `<${el.tag} v-model="${value}">: ` +
|
|
|
+ `v-model is not supported on this element type. ` +
|
|
|
+ 'If you are working with contenteditable, it\'s recommended to ' +
|
|
|
+ 'wrap a library dedicated for that purpose inside a custom component.'
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
// ensure runtime directive metadata
|
|
|
@@ -107,6 +119,41 @@ function genRadioModel (
|
|
|
addHandler(el, 'click', genAssignmentCode(value, valueBinding), null, true)
|
|
|
}
|
|
|
|
|
|
+function genSelect (
|
|
|
+ el: ASTElement,
|
|
|
+ value: string,
|
|
|
+ modifiers: ?ASTModifiers
|
|
|
+) {
|
|
|
+ if (process.env.NODE_ENV !== 'production') {
|
|
|
+ el.children.some(checkOptionWarning)
|
|
|
+ }
|
|
|
+
|
|
|
+ const number = modifiers && modifiers.number
|
|
|
+ const selectedVal = `Array.prototype.filter` +
|
|
|
+ `.call($event.target.options,function(o){return o.selected})` +
|
|
|
+ `.map(function(o){var val = "_value" in o ? o._value : o.value;` +
|
|
|
+ `return ${number ? '_n(val)' : 'val'}})`
|
|
|
+
|
|
|
+ const assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'
|
|
|
+ let code = `var $$selectedVal = ${selectedVal};`
|
|
|
+ code = `${code} ${genAssignmentCode(value, assignment)}`
|
|
|
+ addHandler(el, 'change', code, null, true)
|
|
|
+}
|
|
|
+
|
|
|
+function checkOptionWarning (option: any): boolean {
|
|
|
+ if (option.type === 1 &&
|
|
|
+ option.tag === 'option' &&
|
|
|
+ option.attrsMap.selected != null) {
|
|
|
+ warn(
|
|
|
+ `<select v-model="${option.parent.attrsMap['v-model']}">:\n` +
|
|
|
+ 'inline selected attributes on <option> will be ignored when using v-model. ' +
|
|
|
+ 'Declare initial values in the component\'s data option instead.'
|
|
|
+ )
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
function genDefaultModel (
|
|
|
el: ASTElement,
|
|
|
value: string,
|
|
|
@@ -133,60 +180,46 @@ function genDefaultModel (
|
|
|
const { lazy, number, trim } = modifiers || {}
|
|
|
const event = lazy || (isIE && type === 'range') ? 'change' : 'input'
|
|
|
const needCompositionGuard = !lazy && type !== 'range'
|
|
|
- const isNative = el.tag === 'input' || el.tag === 'textarea'
|
|
|
|
|
|
- let valueExpression = isNative
|
|
|
- ? `$event.target.value${trim ? '.trim()' : ''}`
|
|
|
- : trim ? `(typeof $event === 'string' ? $event.trim() : $event)` : `$event`
|
|
|
- valueExpression = number || type === 'number'
|
|
|
- ? `_n(${valueExpression})`
|
|
|
- : valueExpression
|
|
|
+ let valueExpression = '$event.target.value'
|
|
|
+ if (trim) {
|
|
|
+ valueExpression = `$event.target.value.trim()`
|
|
|
+ }
|
|
|
+ if (number) {
|
|
|
+ valueExpression = `_n(${valueExpression})`
|
|
|
+ }
|
|
|
|
|
|
let code = genAssignmentCode(value, valueExpression)
|
|
|
- if (isNative && needCompositionGuard) {
|
|
|
+ if (needCompositionGuard) {
|
|
|
code = `if($event.target.composing)return;${code}`
|
|
|
}
|
|
|
|
|
|
- addProp(el, 'value', isNative ? `_s(${value})` : `(${value})`)
|
|
|
+ addProp(el, 'value', `(${value})`)
|
|
|
addHandler(el, event, code, null, true)
|
|
|
if (trim || number || type === 'number') {
|
|
|
addHandler(el, 'blur', '$forceUpdate()')
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function genSelect (
|
|
|
- el: ASTElement,
|
|
|
- value: string,
|
|
|
- modifiers: ?ASTModifiers
|
|
|
-) {
|
|
|
- if (process.env.NODE_ENV !== 'production') {
|
|
|
- el.children.some(checkOptionWarning)
|
|
|
- }
|
|
|
-
|
|
|
- const number = modifiers && modifiers.number
|
|
|
- const selectedVal = `Array.prototype.filter` +
|
|
|
- `.call($event.target.options,function(o){return o.selected})` +
|
|
|
- `.map(function(o){var val = "_value" in o ? o._value : o.value;` +
|
|
|
- `return ${number ? '_n(val)' : 'val'}})`
|
|
|
+function genComponentModel (
|
|
|
+ el: ASTElement,
|
|
|
+ value: string,
|
|
|
+ modifiers: ?ASTModifiers
|
|
|
+): ?boolean {
|
|
|
+ const { number, trim } = modifiers || {}
|
|
|
|
|
|
- const assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'
|
|
|
- let code = `var $$selectedVal = ${selectedVal};`
|
|
|
- code = `${code} ${genAssignmentCode(value, assignment)}`
|
|
|
- addHandler(el, 'change', code, null, true)
|
|
|
-}
|
|
|
+ let valueExpression = 'value'
|
|
|
+ if (trim) {
|
|
|
+ valueExpression = `(typeof value === 'string' ? value.trim() : value)`
|
|
|
+ }
|
|
|
+ if (number) {
|
|
|
+ valueExpression = `_n(${valueExpression})`
|
|
|
+ }
|
|
|
|
|
|
-function checkOptionWarning (option: any): boolean {
|
|
|
- if (option.type === 1 &&
|
|
|
- option.tag === 'option' &&
|
|
|
- option.attrsMap.selected != null) {
|
|
|
- warn(
|
|
|
- `<select v-model="${option.parent.attrsMap['v-model']}">:\n` +
|
|
|
- 'inline selected attributes on <option> will be ignored when using v-model. ' +
|
|
|
- 'Declare initial values in the component\'s data option instead.'
|
|
|
- )
|
|
|
- return true
|
|
|
+ el.model = {
|
|
|
+ value,
|
|
|
+ callback: `function (value) {${genAssignmentCode(value, valueExpression)}}`
|
|
|
}
|
|
|
- return false
|
|
|
}
|
|
|
|
|
|
function genAssignmentCode (value: string, assignment: string): string {
|