mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-04 21:17:55 +08:00
test for v-model text
This commit is contained in:
parent
20f6e6e3fe
commit
056cb7f295
@ -182,7 +182,7 @@ function genDirectives (el: ASTElement): string | void {
|
||||
if (gen) {
|
||||
// compile-time directive that manipulates AST.
|
||||
// returns true if it also needs a runtime counterpart.
|
||||
needRuntime = !!gen(el, dir)
|
||||
needRuntime = !!gen(el, dir, warn)
|
||||
}
|
||||
if (needRuntime) {
|
||||
hasRuntime = true
|
||||
|
@ -2,7 +2,11 @@
|
||||
|
||||
import { addHandler, addProp, getBindingAttr } from 'compiler/helpers'
|
||||
|
||||
export default function model (el: ASTElement, dir: ASTDirective) {
|
||||
export default function model (
|
||||
el: ASTElement,
|
||||
dir: ASTDirective,
|
||||
warn: Function
|
||||
): ?boolean {
|
||||
const value = dir.value
|
||||
const modifiers = dir.modifiers
|
||||
if (el.tag === 'select') {
|
||||
@ -20,12 +24,12 @@ export default function model (el: ASTElement, dir: ASTDirective) {
|
||||
genRadioModel(el, value)
|
||||
break
|
||||
default:
|
||||
return genDefaultModel(el, value, modifiers)
|
||||
return genDefaultModel(el, value, modifiers, warn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function genCheckboxModel (el, value) {
|
||||
function genCheckboxModel (el: ASTElement, value: ?string) {
|
||||
const valueBinding = getBindingAttr(el, 'value')
|
||||
addProp(el, 'checked',
|
||||
`Array.isArray(${value})` +
|
||||
@ -45,13 +49,35 @@ function genCheckboxModel (el, value) {
|
||||
)
|
||||
}
|
||||
|
||||
function genRadioModel (el, value) {
|
||||
function genRadioModel (el: ASTElement, value: ?string) {
|
||||
const valueBinding = getBindingAttr(el, 'value')
|
||||
addProp(el, 'checked', `(${value}==${valueBinding})`)
|
||||
addHandler(el, 'change', `${value}=${valueBinding}`)
|
||||
}
|
||||
|
||||
function genDefaultModel (el, value, modifiers) {
|
||||
function genDefaultModel (
|
||||
el: ASTElement,
|
||||
value: ?string,
|
||||
modifiers: ?Object,
|
||||
warn: Function
|
||||
): ?boolean {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (el.tag === 'input' && el.attrsMap.value) {
|
||||
warn(
|
||||
`<${el.tag} v-model="${value}" value="${el.attrsMap.value}">:\n` +
|
||||
'inline value attributes will be ignored when using v-model. ' +
|
||||
'Declare initial values in the component\'s data option instead.'
|
||||
)
|
||||
}
|
||||
if (el.tag === 'textarea' && el.children.length) {
|
||||
warn(
|
||||
`<textarea v-model="${value}">:\n` +
|
||||
'inline content inside <textarea> will be ignored when using v-model. ' +
|
||||
'Declare initial values in the component\'s data option instead.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const type = el.attrsMap.type
|
||||
const { lazy, number, trim } = modifiers || {}
|
||||
const event = lazy ? 'change' : 'input'
|
||||
@ -77,7 +103,7 @@ const getSelectedValueCode =
|
||||
'.call($event.target.options,function(o){return o.selected})' +
|
||||
'.map(function(o){return "_value" in o ? o._value : o.value})'
|
||||
|
||||
function patchChildOptions (el, fn) {
|
||||
function patchChildOptions (el: ASTElement, fn: (arg: ?string) => string) {
|
||||
for (let i = 0; i < el.children.length; i++) {
|
||||
const c = el.children[i]
|
||||
if (c.type === 1 && c.tag === 'option') {
|
||||
@ -86,12 +112,12 @@ function patchChildOptions (el, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
function genSelect (el, value) {
|
||||
function genSelect (el: ASTElement, value: ?string) {
|
||||
addHandler(el, 'change', `${value}=${getSelectedValueCode}[0]`)
|
||||
patchChildOptions(el, valueBinding => `$(${value})===(${valueBinding})`)
|
||||
}
|
||||
|
||||
function genMultiSelect (el, value) {
|
||||
function genMultiSelect (el: ASTElement, value: ?string) {
|
||||
addHandler(el, 'change', `${value}=${getSelectedValueCode}`)
|
||||
patchChildOptions(el, valueBinding => `$(${value}).indexOf(${valueBinding})>-1`)
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
* properties to Elements.
|
||||
*/
|
||||
|
||||
import { warn } from 'core/util/index'
|
||||
import { isAndroid, isIE9 } from 'web/util/index'
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (isIE9) {
|
||||
// http://www.matts411.com/post/internet-explorer-9-oninput/
|
||||
document.addEventListener('selectionchange', () => {
|
||||
@ -16,7 +18,18 @@ if (isIE9) {
|
||||
}
|
||||
|
||||
export default {
|
||||
bind (el) {
|
||||
bind (el, value, vnode) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const tag = el.tagName.toLowerCase()
|
||||
if (!tag.match(/input|select|textarea/)) {
|
||||
warn(
|
||||
`v-model is not supported on element type: <${tag}>. ` +
|
||||
'If you are working with contenteditable, it\'s recommended to ' +
|
||||
'wrap a library dedicated for that purpose inside a custom component.',
|
||||
vnode.context
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!isAndroid) {
|
||||
el.addEventListener('compositionstart', onCompositionStart)
|
||||
el.addEventListener('compositionend', onCompositionEnd)
|
||||
|
6
test/helpers/trigger-event.js
Normal file
6
test/helpers/trigger-event.js
Normal file
@ -0,0 +1,6 @@
|
||||
window.triggerEvent = function triggerEvent (target, event, process) {
|
||||
var e = document.createEvent('HTMLEvents')
|
||||
e.initEvent(event, true, true)
|
||||
if (process) process(e)
|
||||
target.dispatchEvent(e)
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
"jasmine": true
|
||||
},
|
||||
"globals": {
|
||||
"waitForUpdate": true
|
||||
"waitForUpdate": true,
|
||||
"triggerEvent": true
|
||||
}
|
||||
}
|
||||
|
136
test/unit/features/directives/model-text.spec.js
Normal file
136
test/unit/features/directives/model-text.spec.js
Normal file
@ -0,0 +1,136 @@
|
||||
import Vue from 'vue'
|
||||
import { isIE9, isAndroid } from 'web/util/index'
|
||||
|
||||
describe('Directive v-model text', () => {
|
||||
it('should update value both ways', done => {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'b'
|
||||
},
|
||||
template: '<input v-model="test">'
|
||||
}).$mount()
|
||||
expect(vm.$el.value).toBe('b')
|
||||
vm.test = 'a'
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.value).toBe('a')
|
||||
vm.$el.value = 'c'
|
||||
triggerEvent(vm.$el, 'input')
|
||||
expect(vm.test).toBe('c')
|
||||
}).then(done)
|
||||
})
|
||||
|
||||
it('.lazy modifier', () => {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'b'
|
||||
},
|
||||
template: '<input v-model.lazy="test">'
|
||||
}).$mount()
|
||||
expect(vm.$el.value).toBe('b')
|
||||
expect(vm.test).toBe('b')
|
||||
vm.$el.value = 'c'
|
||||
triggerEvent(vm.$el, 'input')
|
||||
expect(vm.test).toBe('b')
|
||||
triggerEvent(vm.$el, 'change')
|
||||
expect(vm.test).toBe('c')
|
||||
})
|
||||
|
||||
it('.number modifier', () => {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 1
|
||||
},
|
||||
template: '<input v-model.number="test">'
|
||||
}).$mount()
|
||||
expect(vm.test).toBe(1)
|
||||
vm.$el.value = '2'
|
||||
triggerEvent(vm.$el, 'input')
|
||||
expect(vm.test).toBe(2)
|
||||
})
|
||||
|
||||
it('.trim modifier', () => {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'hi'
|
||||
},
|
||||
template: '<input v-model.trim="test">'
|
||||
}).$mount()
|
||||
expect(vm.test).toBe('hi')
|
||||
vm.$el.value = ' what '
|
||||
triggerEvent(vm.$el, 'input')
|
||||
expect(vm.test).toBe('what')
|
||||
})
|
||||
|
||||
if (isIE9) {
|
||||
it('cut and delete', done => {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'foo'
|
||||
},
|
||||
template: '<input v-model="test">'
|
||||
}).$mount()
|
||||
const input = vm.$el
|
||||
input.value = 'bar'
|
||||
triggerEvent(input, 'cut')
|
||||
waitForUpdate(() => {
|
||||
expect(vm.test).toBe('bar')
|
||||
input.value = 'a'
|
||||
triggerEvent(input, 'keyup', (e) => { e.keyCode = 8 })
|
||||
expect(vm.test).toBe('a')
|
||||
}).then(done)
|
||||
})
|
||||
}
|
||||
|
||||
if (!isAndroid) {
|
||||
it('compositionevents', function (done) {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'foo'
|
||||
},
|
||||
template: '<input v-model="test">'
|
||||
}).$mount()
|
||||
const input = vm.$el
|
||||
triggerEvent(input, 'compositionstart')
|
||||
input.value = 'baz'
|
||||
// input before composition unlock should not call set
|
||||
triggerEvent(input, 'input')
|
||||
expect(vm.test).toBe('foo')
|
||||
// after composition unlock it should work
|
||||
triggerEvent(input, 'compositionend')
|
||||
triggerEvent(input, 'input')
|
||||
expect(vm.test).toBe('baz')
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
it('warn inline value attribute', () => {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'foo'
|
||||
},
|
||||
template: '<input v-model="test" value="bar">'
|
||||
}).$mount()
|
||||
expect(vm.test).toBe('foo')
|
||||
expect(vm.$el.value).toBe('foo')
|
||||
expect('inline value attributes will be ignored').toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('warn textarea inline content', function () {
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
test: 'foo'
|
||||
},
|
||||
template: '<textarea v-model="test">bar</textarea>'
|
||||
}).$mount()
|
||||
expect(vm.test).toBe('foo')
|
||||
expect(vm.$el.value).toBe('foo')
|
||||
expect('inline content inside <textarea> will be ignored').toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('warn invalid tag', () => {
|
||||
new Vue({
|
||||
template: '<div v-model="test"></div>'
|
||||
}).$mount()
|
||||
expect('v-model is not supported on element type: <div>').toHaveBeenWarned()
|
||||
})
|
||||
})
|
@ -1,6 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
import '../helpers/to-have-been-warned.js'
|
||||
import '../helpers/wait-for-update.js'
|
||||
import '../helpers/trigger-event.js'
|
||||
|
||||
Vue.config.preserveWhitespace = false
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user