Skip to content

Commit

Permalink
fix(v-on): return handler value when using modifiers (vuejs#7704)
Browse files Browse the repository at this point in the history
  • Loading branch information
STUkh authored and aJean committed Aug 19, 2020
1 parent 7d9bd1f commit 7a0bb19
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/compiler/codegen/events.js
Expand Up @@ -132,9 +132,9 @@ function genHandler (
code += genModifierCode
}
const handlerCode = isMethodPath
? handler.value + '($event)'
? `return ${handler.value}($event)`
: isFunctionExpression
? `(${handler.value})($event)`
? `return (${handler.value})($event)`
: handler.value
/* istanbul ignore if */
if (__WEEX__ && handler.params) {
Expand Down
91 changes: 68 additions & 23 deletions test/unit/modules/compiler/codegen.spec.js
Expand Up @@ -269,120 +269,165 @@ describe('codegen', () => {
)
})

it('generate events with method call', () => {
assertCodegen(
'<input @input="onInput($event);">',
`with(this){return _c('input',{on:{"input":function($event){onInput($event);}}})}`
)
// empty arguments
assertCodegen(
'<input @input="onInput();">',
`with(this){return _c('input',{on:{"input":function($event){onInput();}}})}`
)
// without semicolon
assertCodegen(
'<input @input="onInput($event)">',
`with(this){return _c('input',{on:{"input":function($event){onInput($event)}}})}`
)
// multiple args
assertCodegen(
'<input @input="onInput($event, \'abc\', 5);">',
`with(this){return _c('input',{on:{"input":function($event){onInput($event, 'abc', 5);}}})}`
)
// expression in args
assertCodegen(
'<input @input="onInput($event, 2+2);">',
`with(this){return _c('input',{on:{"input":function($event){onInput($event, 2+2);}}})}`
)
// tricky symbols in args
assertCodegen(
'<input @input="onInput(\');[\'());\');">',
`with(this){return _c('input',{on:{"input":function($event){onInput(');[\'());');}}})}`
)
})

it('generate events with multiple statements', () => {
// normal function
assertCodegen(
'<input @input="onInput1();onInput2()">',
`with(this){return _c('input',{on:{"input":function($event){onInput1();onInput2()}}})}`
)
// function with multiple args
assertCodegen(
'<input @input="onInput1($event, \'text\');onInput2(\'text2\', $event)">',
`with(this){return _c('input',{on:{"input":function($event){onInput1($event, 'text');onInput2('text2', $event)}}})}`
)
})

it('generate events with keycode', () => {
assertCodegen(
'<input @input.enter="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;return onInput($event)}}})}`
)
// multiple keycodes (delete)
assertCodegen(
'<input @input.delete="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;return onInput($event)}}})}`
)
// multiple keycodes (chained)
assertCodegen(
'<input @keydown.enter.delete="onInput">',
`with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter")&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;onInput($event)}}})}`
`with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter")&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;return onInput($event)}}})}`
)
// number keycode
assertCodegen(
'<input @input.13="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&$event.keyCode!==13)return null;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&$event.keyCode!==13)return null;return onInput($event)}}})}`
)
// custom keycode
assertCodegen(
'<input @input.custom="onInput">',
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key,undefined))return null;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key,undefined))return null;return onInput($event)}}})}`
)
})

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

// GitHub Issues #5146
it('generate events with generic modifiers and keycode correct order', () => {
assertCodegen(
'<input @keydown.enter.prevent="onInput">',
`with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.preventDefault();onInput($event)}}})}`
`with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.preventDefault();return onInput($event)}}})}`
)

assertCodegen(
'<input @keydown.enter.stop="onInput">',
`with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.stopPropagation();onInput($event)}}})}`
`with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.stopPropagation();return 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 null;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return null;return onClick($event)}}})}`
)
assertCodegen(
'<input @click.shift="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.shiftKey)return null;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.shiftKey)return null;return onClick($event)}}})}`
)
assertCodegen(
'<input @click.alt="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.altKey)return null;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.altKey)return null;return onClick($event)}}})}`
)
assertCodegen(
'<input @click.meta="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.metaKey)return null;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.metaKey)return null;return onClick($event)}}})}`
)
assertCodegen(
'<input @click.exact="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey)return null;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey)return null;return onClick($event)}}})}`
)
assertCodegen(
'<input @click.ctrl.exact="onClick">',
`with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return null;if($event.shiftKey||$event.altKey||$event.metaKey)return null;onClick($event)}}})}`
`with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return null;if($event.shiftKey||$event.altKey||$event.metaKey)return null;return onClick($event)}}})}`
)
})

it('generate events with multiple modifiers', () => {
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 null;onInput($event)}}})}`
`with(this){return _c('input',{on:{"input":function($event){$event.stopPropagation();$event.preventDefault();if($event.target !== $event.currentTarget)return null;return onInput($event)}}})}`
)
})

it('generate events with capture modifier', () => {
assertCodegen(
'<input @input.capture="onInput">',
`with(this){return _c('input',{on:{"!input":function($event){onInput($event)}}})}`
`with(this){return _c('input',{on:{"!input":function($event){return onInput($event)}}})}`
)
})

it('generate events with once modifier', () => {
assertCodegen(
'<input @input.once="onInput">',
`with(this){return _c('input',{on:{"~input":function($event){onInput($event)}}})}`
`with(this){return _c('input',{on:{"~input":function($event){return onInput($event)}}})}`
)
})

it('generate events with capture and once modifier', () => {
assertCodegen(
'<input @input.capture.once="onInput">',
`with(this){return _c('input',{on:{"~!input":function($event){onInput($event)}}})}`
`with(this){return _c('input',{on:{"~!input":function($event){return onInput($event)}}})}`
)
})

it('generate events with once and capture modifier', () => {
assertCodegen(
'<input @input.once.capture="onInput">',
`with(this){return _c('input',{on:{"~!input":function($event){onInput($event)}}})}`
`with(this){return _c('input',{on:{"~!input":function($event){return onInput($event)}}})}`
)
})

Expand Down Expand Up @@ -427,7 +472,7 @@ describe('codegen', () => {
// with modifiers
assertCodegen(
`<input @keyup.enter="e=>current++">`,
`with(this){return _c('input',{on:{"keyup":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;(e=>current++)($event)}}})}`
`with(this){return _c('input',{on:{"keyup":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;return (e=>current++)($event)}}})}`
)
})

Expand All @@ -452,7 +497,7 @@ describe('codegen', () => {
it('generate multiple event handlers', () => {
assertCodegen(
'<input @input="current++" @input.stop="onInput">',
`with(this){return _c('input',{on:{"input":[function($event){current++},function($event){$event.stopPropagation();onInput($event)}]}})}`
`with(this){return _c('input',{on:{"input":[function($event){current++},function($event){$event.stopPropagation();return onInput($event)}]}})}`
)
})

Expand Down
37 changes: 37 additions & 0 deletions test/unit/modules/vdom/patch/edge-cases.spec.js
Expand Up @@ -25,6 +25,43 @@ describe('vdom patch: edge cases', () => {
}).then(done)
})

// exposed by #7705
// methods and function expressions with modifiers should return result instead of undefined
// skipped odd children[1,3, ...] because they are rendered as text nodes with undefined value
it('should return listener\'s result for method name and function expression with and w/o modifiers', done => {
const dummyEvt = { preventDefault: () => {} }
new Vue({
template: `
<div v-test>
<div @click="addFive"></div>
<div @click.prevent="addFive"></div>
<div @click="addFive($event, 5)"></div>
<div @click.prevent="addFive($event, 5)"></div>
</div>
`,
methods: {
addFive ($event, toAdd = 0) {
return toAdd + 5
}
},
directives: {
test: {
bind (el, binding, vnode) {
waitForUpdate(() => {
expect(vnode.children[0].data.on.click()).toBe(5)
}).then(() => {
expect(vnode.children[2].data.on.click(dummyEvt)).toBe(5)
}).then(() => {
expect(vnode.children[4].data.on.click()).not.toBeDefined()
}).then(() => {
expect(vnode.children[6].data.on.click(dummyEvt)).not.toBeDefined()
}).then(done)
}
}
}
}).$mount()
})

// #3533
// a static node is reused in createElm, which changes its elm reference
// and is inserted into a different parent.
Expand Down

0 comments on commit 7a0bb19

Please sign in to comment.