Skip to content

Commit

Permalink
fix .once with other modifiers that prevent execution of a handler (fix
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 14, 2017
1 parent f59aef0 commit 05c769b
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 21 deletions.
17 changes: 10 additions & 7 deletions src/compiler/codegen/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ const keyCodes: { [key: string]: number | Array<number> } = {
const modifierCode: { [key: string]: string } = {
stop: '$event.stopPropagation();',
prevent: '$event.preventDefault();',
self: 'if($event.target !== $event.currentTarget)return;',
ctrl: 'if(!$event.ctrlKey)return;',
shift: 'if(!$event.shiftKey)return;',
alt: 'if(!$event.altKey)return;',
meta: 'if(!$event.metaKey)return;'
// #4868: modifiers that prevent the execution of the listener
// need to explicitly return null so that we can determine whether to remove
// the listener for .once
self: 'if($event.target !== $event.currentTarget)return null;',
ctrl: 'if(!$event.ctrlKey)return null;',
shift: 'if(!$event.shiftKey)return null;',
alt: 'if(!$event.altKey)return null;',
meta: 'if(!$event.metaKey)return null;'
}

export function genHandlers (events: ASTElementHandlers, native?: boolean): string {
Expand Down Expand Up @@ -62,12 +65,12 @@ function genHandler (
const handlerCode = simplePathRE.test(handler.value)
? handler.value + '($event)'
: handler.value
return 'function($event){' + code + handlerCode + '}'
return `function($event){${code}${handlerCode}}`
}
}

function genKeyFilter (keys: Array<string>): string {
return `if(${keys.map(genFilterCode).join('&&')})return;`
return `if(${keys.map(genFilterCode).join('&&')})return null;`
}

function genFilterCode (key: string): string {
Expand Down
3 changes: 2 additions & 1 deletion src/core/vdom/helpers/update-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ function createEventHandle (fn: Function | Array<Function>): {
fn[i].apply(null, arguments)
}
} else {
fn.apply(null, arguments)
// return handler return value for single handlers
return fn.apply(null, arguments)
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/platforms/web/runtime/modules/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ function add (
const oldHandler = handler
const _target = target // save current target element in closure
handler = function (ev) {
remove(event, handler, capture, _target)
arguments.length === 1
const res = arguments.length === 1
? oldHandler(ev)
: oldHandler.apply(null, arguments)
if (res !== null) {
remove(event, handler, capture, _target)
}
}
}
target.addEventListener(event, handler, capture)
Expand Down
15 changes: 15 additions & 0 deletions test/unit/features/directives/on.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,21 @@ describe('Directive v-on', () => {
expect(callOrder.toString()).toBe('1,2,2')
})

// #4846
it('should support once and other modifiers', () => {
vm = new Vue({
el,
template: `<div @click.once.self="foo"><span/></div>`,
methods: { foo: spy }
})
triggerEvent(vm.$el.firstChild, 'click')
expect(spy).not.toHaveBeenCalled()
triggerEvent(vm.$el, 'click')
expect(spy).toHaveBeenCalled()
triggerEvent(vm.$el, 'click')
expect(spy.calls.count()).toBe(1)
})

it('should support keyCode', () => {
vm = new Vue({
el,
Expand Down
22 changes: 11 additions & 11 deletions test/unit/modules/compiler/codegen.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,27 +229,27 @@ describe('codegen', () => {
it('generate events with keycode', () => {
assertCodegen(
'<input @input.enter="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(_k($event.keyCode,"enter",13))return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(_k($event.keyCode,"enter",13))return null;onInput($event)}}})}`
)
// multiple keycodes (delete)
assertCodegen(
'<input @input.delete="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(_k($event.keyCode,"delete",[8,46]))return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(_k($event.keyCode,"delete",[8,46]))return null;onInput($event)}}})}`
)
// multiple keycodes (chained)
assertCodegen(
'<input @keydown.enter.delete="onInput">',
`with(this){return _c('input',{on:{"keydown":function($event){if(_k($event.keyCode,"enter",13)&&_k($event.keyCode,"delete",[8,46]))return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"keydown":function($event){if(_k($event.keyCode,"enter",13)&&_k($event.keyCode,"delete",[8,46]))return null;onInput($event)}}})}`
)
// number keycode
assertCodegen(
'<input @input.13="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if($event.keyCode!==13)return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if($event.keyCode!==13)return null;onInput($event)}}})}`
)
// custom keycode
assertCodegen(
'<input @input.custom="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(_k($event.keyCode,"custom"))return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(_k($event.keyCode,"custom"))return null;onInput($event)}}})}`
)
})

Expand All @@ -264,33 +264,33 @@ describe('codegen', () => {
)
assertCodegen(
'<input @input.self="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if($event.target !== $event.currentTarget)return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if($event.target !== $event.currentTarget)return null;onInput($event)}}})}`
)
})

it('generate events with mouse event modifiers', () => {
assertCodegen(
'<input @click.ctrl="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return null;onClick($event)}}})}`
)
assertCodegen(
'<input @click.shift="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.shiftKey)return;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.shiftKey)return null;onClick($event)}}})}`
)
assertCodegen(
'<input @click.alt="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.altKey)return;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.altKey)return null;onClick($event)}}})}`
)
assertCodegen(
'<input @click.meta="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.metaKey)return;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.metaKey)return null;onClick($event)}}})}`
)
})

it('generate events with multiple modifers', () => {
assertCodegen(
'<input @input.stop.prevent.self="onInput">',
`with(this){return _c('input',{on:{"input":function($event){$event.stopPropagation();$event.preventDefault();if($event.target !== $event.currentTarget)return;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){$event.stopPropagation();$event.preventDefault();if($event.target !== $event.currentTarget)return null;onInput($event)}}})}`
)
})

Expand Down

0 comments on commit 05c769b

Please sign in to comment.