You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.3 KiB
147 lines
4.3 KiB
/**
|
|
* Not type checking this file because flow doesn't like attaching
|
|
* properties to Elements.
|
|
*/
|
|
|
|
import { isTextInputType } from 'web/util/element'
|
|
import { looseEqual, looseIndexOf } from 'shared/util'
|
|
import { mergeVNodeHook } from 'core/vdom/helpers/index'
|
|
import { warn, isIE9, isIE, isEdge } from 'core/util/index'
|
|
|
|
/* istanbul ignore if */
|
|
if (isIE9) {
|
|
// http://www.matts411.com/post/internet-explorer-9-oninput/
|
|
document.addEventListener('selectionchange', () => {
|
|
const el = document.activeElement
|
|
if (el && el.vmodel) {
|
|
trigger(el, 'input')
|
|
}
|
|
})
|
|
}
|
|
|
|
const directive = {
|
|
inserted (el, binding, vnode, oldVnode) {
|
|
if (vnode.tag === 'select') {
|
|
// #6903
|
|
if (oldVnode.elm && !oldVnode.elm._vOptions) {
|
|
mergeVNodeHook(vnode, 'postpatch', () => {
|
|
directive.componentUpdated(el, binding, vnode)
|
|
})
|
|
} else {
|
|
setSelected(el, binding, vnode.context)
|
|
}
|
|
el._vOptions = [].map.call(el.options, getValue)
|
|
} else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
|
|
el._vModifiers = binding.modifiers
|
|
if (!binding.modifiers.lazy) {
|
|
el.addEventListener('compositionstart', onCompositionStart)
|
|
el.addEventListener('compositionend', onCompositionEnd)
|
|
// Safari < 10.2 & UIWebView doesn't fire compositionend when
|
|
// switching focus before confirming composition choice
|
|
// this also fixes the issue where some browsers e.g. iOS Chrome
|
|
// fires "change" instead of "input" on autocomplete.
|
|
el.addEventListener('change', onCompositionEnd)
|
|
/* istanbul ignore if */
|
|
if (isIE9) {
|
|
el.vmodel = true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
componentUpdated (el, binding, vnode) {
|
|
if (vnode.tag === 'select') {
|
|
setSelected(el, binding, vnode.context)
|
|
// in case the options rendered by v-for have changed,
|
|
// it's possible that the value is out-of-sync with the rendered options.
|
|
// detect such cases and filter out values that no longer has a matching
|
|
// option in the DOM.
|
|
const prevOptions = el._vOptions
|
|
const curOptions = el._vOptions = [].map.call(el.options, getValue)
|
|
if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
|
|
// trigger change event if
|
|
// no matching option found for at least one value
|
|
const needReset = el.multiple
|
|
? binding.value.some(v => hasNoMatchingOption(v, curOptions))
|
|
: binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions)
|
|
if (needReset) {
|
|
trigger(el, 'change')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function setSelected (el, binding, vm) {
|
|
actuallySetSelected(el, binding, vm)
|
|
/* istanbul ignore if */
|
|
if (isIE || isEdge) {
|
|
setTimeout(() => {
|
|
actuallySetSelected(el, binding, vm)
|
|
}, 0)
|
|
}
|
|
}
|
|
|
|
function actuallySetSelected (el, binding, vm) {
|
|
const value = binding.value
|
|
const isMultiple = el.multiple
|
|
if (isMultiple && !Array.isArray(value)) {
|
|
process.env.NODE_ENV !== 'production' && warn(
|
|
`<select multiple v-model="${binding.expression}"> ` +
|
|
`expects an Array value for its binding, but got ${
|
|
Object.prototype.toString.call(value).slice(8, -1)
|
|
}`,
|
|
vm
|
|
)
|
|
return
|
|
}
|
|
let selected, option
|
|
for (let i = 0, l = el.options.length; i < l; i++) {
|
|
option = el.options[i]
|
|
if (isMultiple) {
|
|
selected = looseIndexOf(value, getValue(option)) > -1
|
|
if (option.selected !== selected) {
|
|
option.selected = selected
|
|
}
|
|
} else {
|
|
if (looseEqual(getValue(option), value)) {
|
|
if (el.selectedIndex !== i) {
|
|
el.selectedIndex = i
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if (!isMultiple) {
|
|
el.selectedIndex = -1
|
|
}
|
|
}
|
|
|
|
function hasNoMatchingOption (value, options) {
|
|
return options.every(o => !looseEqual(o, value))
|
|
}
|
|
|
|
function getValue (option) {
|
|
return '_value' in option
|
|
? option._value
|
|
: option.value
|
|
}
|
|
|
|
function onCompositionStart (e) {
|
|
e.target.composing = true
|
|
}
|
|
|
|
function onCompositionEnd (e) {
|
|
// prevent triggering an input event for no reason
|
|
if (!e.target.composing) return
|
|
e.target.composing = false
|
|
trigger(e.target, 'input')
|
|
}
|
|
|
|
function trigger (el, type) {
|
|
const e = document.createEvent('HTMLEvents')
|
|
e.initEvent(type, true, true)
|
|
el.dispatchEvent(e)
|
|
}
|
|
|
|
export default directive
|
|
|