From 8187bc92732e2b052cd924824343bbbdf562194d Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Mon, 16 Jan 2023 08:34:43 +0100 Subject: [PATCH] [FIX] compiler: stop event handlers looking up __owl__ in render context This is another place where we were looking up the component node in the rendering context instead of on the component, which causes handlers to have the wrong this when within a t-call-context. --- src/compiler/code_generator.ts | 2 +- .../__snapshots__/event_handling.test.ts.snap | 72 +++++++++---------- .../compiler/__snapshots__/misc.test.ts.snap | 22 +++--- tests/compiler/event_handling.test.ts | 12 +++- .../__snapshots__/basics.test.ts.snap | 4 +- .../__snapshots__/concurrency.test.ts.snap | 18 ++--- .../__snapshots__/event_handling.test.ts.snap | 16 ++--- .../higher_order_component.test.ts.snap | 2 +- .../__snapshots__/lifecycle.test.ts.snap | 2 +- .../__snapshots__/props.test.ts.snap | 4 +- .../__snapshots__/refs.test.ts.snap | 2 +- .../__snapshots__/slots.test.ts.snap | 18 ++--- .../__snapshots__/t_call.test.ts.snap | 35 +++++++-- .../__snapshots__/t_component.test.ts.snap | 2 +- .../__snapshots__/t_model.test.ts.snap | 10 +-- .../__snapshots__/t_on.test.ts.snap | 34 ++++----- tests/components/t_call.test.ts | 26 +++++++ tests/misc/__snapshots__/portal.test.ts.snap | 2 +- 18 files changed, 171 insertions(+), 112 deletions(-) diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 136aa81d..5949700b 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -539,7 +539,7 @@ export class CodeGenerator { if (modifiers.length) { modifiersCode = `${modifiers.join(",")}, `; } - return `[${modifiersCode}${this.captureExpression(handler)}, ctx]`; + return `[${modifiersCode}${this.captureExpression(handler)}, this]`; } compileTDomNode(ast: ASTDomNode, ctx: Context): string { diff --git a/tests/compiler/__snapshots__/event_handling.test.ts.snap b/tests/compiler/__snapshots__/event_handling.test.ts.snap index e1c13502..91399bd6 100644 --- a/tests/compiler/__snapshots__/event_handling.test.ts.snap +++ b/tests/compiler/__snapshots__/event_handling.test.ts.snap @@ -8,7 +8,7 @@ exports[`t-on can bind event handler 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['add'], ctx]; + let hdlr1 = [ctx['add'], this]; return block1([hdlr1]); } }" @@ -23,7 +23,7 @@ exports[`t-on can bind handlers with arguments 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['add']; - let hdlr1 = [()=>v1(5), ctx]; + let hdlr1 = [()=>v1(5), this]; return block1([hdlr1]); } }" @@ -38,7 +38,7 @@ exports[`t-on can bind handlers with empty object 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['doSomething']; - let hdlr1 = [()=>v1({}), ctx]; + let hdlr1 = [()=>v1({}), this]; return block1([hdlr1]); } }" @@ -53,7 +53,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) return function template(ctx, node, key = \\"\\") { const v1 = ctx['doSomething']; - let hdlr1 = [()=>v1({}), ctx]; + let hdlr1 = [()=>v1({}), this]; return block1([hdlr1]); } }" @@ -77,7 +77,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) const key1 = ctx['action_index']; const v1 = ctx['activate']; const v2 = ctx['action']; - let hdlr1 = [()=>v1(v2), ctx]; + let hdlr1 = [()=>v1(v2), this]; c_block2[i1] = withKey(block3([hdlr1]), key1); } const b2 = list(c_block2); @@ -95,7 +95,7 @@ exports[`t-on can bind handlers with object arguments 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['add']; - let hdlr1 = [()=>v1({val:5}), ctx]; + let hdlr1 = [()=>v1({val:5}), this]; return block1([hdlr1]); } }" @@ -109,8 +109,8 @@ exports[`t-on can bind two event handlers 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['handleClick'], ctx]; - let hdlr2 = [ctx['handleDblClick'], ctx]; + let hdlr1 = [ctx['handleClick'], this]; + let hdlr2 = [ctx['handleDblClick'], this]; return block1([hdlr1, hdlr2]); } }" @@ -124,7 +124,7 @@ exports[`t-on handler is bound to proper owner 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['add'], ctx]; + let hdlr1 = [ctx['add'], this]; return block1([hdlr1]); } }" @@ -144,7 +144,7 @@ exports[`t-on handler is bound to proper owner, part 2 1`] = ` for (let i1 = 0; i1 < l_block1; i1++) { ctx[\`value\`] = v_block1[i1]; const key1 = ctx['value']; - let hdlr1 = [ctx['add'], ctx]; + let hdlr1 = [ctx['add'], this]; c_block1[i1] = withKey(block2([hdlr1]), key1); } return list(c_block1); @@ -172,7 +172,7 @@ exports[`t-on handler is bound to proper owner, part 3 2`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['add'], ctx]; + let hdlr1 = [ctx['add'], this]; return block1([hdlr1]); } }" @@ -210,7 +210,7 @@ exports[`t-on handler is bound to proper owner, part 4 2`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['add'], ctx]; + let hdlr1 = [ctx['add'], this]; return block1([hdlr1]); } }" @@ -224,7 +224,7 @@ exports[`t-on receive event in first argument 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['add'], ctx]; + let hdlr1 = [ctx['add'], this]; return block1([hdlr1]); } }" @@ -238,8 +238,8 @@ exports[`t-on t-on modifiers (native listener) basic support for native listener let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['divClicked'], ctx]; - let hdlr2 = [ctx['btnClicked'], ctx]; + let hdlr1 = [ctx['divClicked'], this]; + let hdlr2 = [ctx['btnClicked'], this]; return block1([hdlr1, hdlr2]); } }" @@ -253,7 +253,7 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-esc 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['onClick'], ctx]; + let hdlr1 = [ctx['onClick'], this]; let txt1 = ctx['text']; return block1([hdlr1, txt1]); } @@ -269,7 +269,7 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-out 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['onClick'], ctx]; + let hdlr1 = [ctx['onClick'], this]; const b2 = safeOutput(ctx['html']); return block1([hdlr1], [b2]); } @@ -284,8 +284,8 @@ exports[`t-on t-on modifiers (native listener) t-on with .capture modifier 1`] = let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [\\"capture\\", ctx['onCapture'], ctx]; - let hdlr2 = [ctx['doSomething'], ctx]; + let hdlr1 = [\\"capture\\", ctx['onCapture'], this]; + let hdlr2 = [ctx['doSomething'], this]; return block1([hdlr1, hdlr2]); } }" @@ -299,7 +299,7 @@ exports[`t-on t-on modifiers (native listener) t-on with empty handler (only mod let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [\\"prevent\\", , ctx]; + let hdlr1 = [\\"prevent\\", , this]; return block1([hdlr1]); } }" @@ -313,7 +313,7 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and self modifi let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [\\"prevent\\",\\"self\\", ctx['onClick'], ctx]; + let hdlr1 = [\\"prevent\\",\\"self\\", ctx['onClick'], this]; return block1([hdlr1]); } }" @@ -327,9 +327,9 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and/or stop mod let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [\\"prevent\\", ctx['onClickPrevented'], ctx]; - let hdlr2 = [\\"stop\\", ctx['onClickStopped'], ctx]; - let hdlr3 = [\\"prevent\\",\\"stop\\", ctx['onClickPreventedAndStopped'], ctx]; + let hdlr1 = [\\"prevent\\", ctx['onClickPrevented'], this]; + let hdlr2 = [\\"stop\\", ctx['onClickStopped'], this]; + let hdlr3 = [\\"prevent\\",\\"stop\\", ctx['onClickPreventedAndStopped'], this]; return block1([hdlr1, hdlr2, hdlr3]); } }" @@ -352,7 +352,7 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent modifier in t-f const key1 = ctx['project']; const v1 = ctx['onEdit']; const v2 = ctx['project']; - let hdlr1 = [\\"prevent\\", _ev=>v1(v2.id,_ev), ctx]; + let hdlr1 = [\\"prevent\\", _ev=>v1(v2.id,_ev), this]; let txt1 = ctx['project'].name; c_block2[i1] = withKey(block3([hdlr1, txt1]), key1); } @@ -370,7 +370,7 @@ exports[`t-on t-on modifiers (native listener) t-on with self and prevent modifi let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [\\"self\\",\\"prevent\\", ctx['onClick'], ctx]; + let hdlr1 = [\\"self\\",\\"prevent\\", ctx['onClick'], this]; return block1([hdlr1]); } }" @@ -384,8 +384,8 @@ exports[`t-on t-on modifiers (native listener) t-on with self modifier 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['onClick'], ctx]; - let hdlr2 = [\\"self\\", ctx['onClickSelf'], ctx]; + let hdlr1 = [ctx['onClick'], this]; + let hdlr2 = [\\"self\\", ctx['onClickSelf'], this]; return block1([hdlr1, hdlr2]); } }" @@ -399,8 +399,8 @@ exports[`t-on t-on modifiers (synthetic listener) basic support for synthetic 1` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [\\"synthetic\\", ctx['divClicked'], ctx]; - let hdlr2 = [\\"synthetic\\", ctx['btnClicked'], ctx]; + let hdlr1 = [\\"synthetic\\", ctx['divClicked'], this]; + let hdlr2 = [\\"synthetic\\", ctx['btnClicked'], this]; return block1([hdlr1, hdlr2]); } }" @@ -415,7 +415,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; - let hdlr1 = [()=>v1.incrementCounter(2), ctx]; + let hdlr1 = [()=>v1.incrementCounter(2), this]; return block1([hdlr1]); } }" @@ -430,7 +430,7 @@ exports[`t-on t-on with inline statement 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; - let hdlr1 = [()=>v1.counter++, ctx]; + let hdlr1 = [()=>v1.counter++, this]; return block1([hdlr1]); } }" @@ -445,7 +445,7 @@ exports[`t-on t-on with inline statement, part 2 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; - let hdlr1 = [()=>v1.flag=!v1.flag, ctx]; + let hdlr1 = [()=>v1.flag=!v1.flag, this]; return block1([hdlr1]); } }" @@ -461,7 +461,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; const v2 = ctx['someFunction']; - let hdlr1 = [()=>v1.n=v2(3), ctx]; + let hdlr1 = [()=>v1.n=v2(3), this]; return block1([hdlr1]); } }" @@ -490,7 +490,7 @@ exports[`t-on t-on with t-call 2`] = ` let block1 = createBlock(\`

lucas

\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['update'], ctx]; + let hdlr1 = [ctx['update'], this]; return block1([hdlr1]); } }" @@ -521,7 +521,7 @@ exports[`t-on t-on, with arguments and t-call 2`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['this']; const v2 = ctx['value']; - let hdlr1 = [()=>v1.update(v2), ctx]; + let hdlr1 = [()=>v1.update(v2), this]; return block1([hdlr1]); } }" diff --git a/tests/compiler/__snapshots__/misc.test.ts.snap b/tests/compiler/__snapshots__/misc.test.ts.snap index 33f7acc2..4328ea96 100644 --- a/tests/compiler/__snapshots__/misc.test.ts.snap +++ b/tests/compiler/__snapshots__/misc.test.ts.snap @@ -228,7 +228,7 @@ exports[`misc other complex template 1`] = ` for (let i1 = 0; i1 < l_block2; i1++) { ctx[\`project\`] = v_block2[i1]; const key1 = ctx['project'].id; - let hdlr1 = [ctx['selectProject'](ctx['project']), ctx]; + let hdlr1 = [ctx['selectProject'](ctx['project']), this]; let txt2 = ctx['project'].name; c_block2[i1] = withKey(block3([hdlr1, txt2]), key1); } @@ -260,8 +260,8 @@ exports[`misc other complex template 1`] = ` } b4 = multi([b5, b6]); } - let hdlr2 = [ctx['toggleSettingsMenu'], ctx]; - let hdlr3 = [ctx['toggleMore'], ctx]; + let hdlr2 = [ctx['toggleSettingsMenu'], this]; + let hdlr3 = [ctx['toggleMore'], this]; if (ctx['categories']&&ctx['categories'].length>1) { ctx = Object.create(ctx); const [k_block15, v_block15, l_block15, c_block15] = prepareList(ctx['categories']);; @@ -278,9 +278,9 @@ exports[`misc other complex template 1`] = ` b14 = block14([], [b15]); } let attr8 = new String((ctx['search'].value) || \\"\\"); - let hdlr4 = [ctx['updateFilter'], ctx]; - let hdlr5 = [ctx['updateFilter'], ctx]; - let hdlr6 = [ctx['clearSearch'], ctx]; + let hdlr4 = [ctx['updateFilter'], this]; + let hdlr5 = [ctx['updateFilter'], this]; + let hdlr6 = [ctx['clearSearch'], this]; if (ctx['triggers']) { ctx = Object.create(ctx); const [k_block18, v_block18, l_block18, c_block18] = prepareList(ctx['triggers']);; @@ -293,7 +293,7 @@ exports[`misc other complex template 1`] = ` let attr10 = \`trigger_\${ctx['trigger'].id}\`; let attr11 = new Boolean(ctx['options'].trigger_display[ctx['trigger'].id]); let attr12 = ctx['trigger'].id; - let hdlr7 = [ctx['updateTriggerDisplay'], ctx]; + let hdlr7 = [ctx['updateTriggerDisplay'], this]; let attr13 = \`trigger_\${ctx['trigger'].id}\`; let txt6 = ctx['trigger'].name; b20 = block20([attr9, attr10, attr11, attr12, hdlr7, attr13, txt6]); @@ -302,10 +302,10 @@ exports[`misc other complex template 1`] = ` } ctx = ctx.__proto__; const b18 = list(c_block18); - let hdlr8 = [ctx['triggerAll'], ctx]; - let hdlr9 = [ctx['triggerNone'], ctx]; - let hdlr10 = [ctx['triggerDefault'], ctx]; - let hdlr11 = [ctx['toggleSettingsMenu'], ctx]; + let hdlr8 = [ctx['triggerAll'], this]; + let hdlr9 = [ctx['triggerNone'], this]; + let hdlr10 = [ctx['triggerDefault'], this]; + let hdlr11 = [ctx['toggleSettingsMenu'], this]; const b21 = block21([hdlr8, hdlr9, hdlr10, hdlr11]); b17 = multi([b18, b21]); } diff --git a/tests/compiler/event_handling.test.ts b/tests/compiler/event_handling.test.ts index 11b98738..e0743177 100644 --- a/tests/compiler/event_handling.test.ts +++ b/tests/compiler/event_handling.test.ts @@ -161,7 +161,9 @@ describe("t-on", () => { owner.__owl__ = node; const fixture = makeTestFixture(); const render = context.getTemplate("main"); - const bdom = render(owner, node); + const ctx = Object.create(owner); + ctx.this = owner; + const bdom = render.call(owner, ctx, node); mount(bdom, fixture); fixture.querySelector("button")!.click(); }); @@ -186,7 +188,9 @@ describe("t-on", () => { owner.__owl__ = node; const fixture = makeTestFixture(); const render = context.getTemplate("main"); - const bdom = render(owner, node); + const ctx = Object.create(owner); + ctx.this = owner; + const bdom = render.call(owner, ctx, node); mount(bdom, fixture); fixture.querySelector("button")!.click(); }); @@ -266,7 +270,9 @@ describe("t-on", () => { const fixture = makeTestFixture(); const render = app.getTemplate("main"); - const bdom = render(owner, node); + const ctx = Object.create(owner); + ctx.this = owner; + const bdom = render.call(owner, ctx, node); mount(bdom, fixture); fixture.querySelector("p")!.click(); }); diff --git a/tests/components/__snapshots__/basics.test.ts.snap b/tests/components/__snapshots__/basics.test.ts.snap index 2a75d381..0245248d 100644 --- a/tests/components/__snapshots__/basics.test.ts.snap +++ b/tests/components/__snapshots__/basics.test.ts.snap @@ -135,7 +135,7 @@ exports[`basics can be clicked on and updated 1`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].counter; const v1 = ctx['state']; - let hdlr1 = [()=>v1.counter++, ctx]; + let hdlr1 = [()=>v1.counter++, this]; return block1([txt1, hdlr1]); } }" @@ -646,7 +646,7 @@ exports[`basics rerendering a widget with a sub widget 2`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].counter; const v1 = ctx['state']; - let hdlr1 = [()=>v1.counter++, ctx]; + let hdlr1 = [()=>v1.counter++, this]; return block1([txt1, hdlr1]); } }" diff --git a/tests/components/__snapshots__/concurrency.test.ts.snap b/tests/components/__snapshots__/concurrency.test.ts.snap index 18df8e4c..a2987996 100644 --- a/tests/components/__snapshots__/concurrency.test.ts.snap +++ b/tests/components/__snapshots__/concurrency.test.ts.snap @@ -93,7 +93,7 @@ exports[`another scenario with delayed rendering 3`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; return block1([hdlr1, txt1]); } @@ -1262,7 +1262,7 @@ exports[`delayed rendering, but then initial rendering is cancelled by yet anoth let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; return block1([hdlr1, txt1]); } @@ -1327,7 +1327,7 @@ exports[`delayed rendering, destruction, stuff happens 4`] = ` return function template(ctx, node, key = \\"\\") { const b2 = text(\`D\`); - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; const b3 = block3([hdlr1, txt1]); return multi([b2, b3]); @@ -1369,7 +1369,7 @@ exports[`delayed rendering, reusing fiber and stuff 3`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; return block1([hdlr1, txt1]); } @@ -1415,7 +1415,7 @@ exports[`delayed rendering, reusing fiber then component is destroyed and stuff let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; return block1([hdlr1, txt1]); } @@ -1459,7 +1459,7 @@ exports[`delayed rendering, then component is destroyed and stuff 3`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; return block1([hdlr1, txt1]); } @@ -1647,7 +1647,7 @@ exports[`rendering component again in next microtick 1`] = ` return function template(ctx, node, key = \\"\\") { let b2; - let hdlr1 = [ctx['onClick'], ctx]; + let hdlr1 = [ctx['onClick'], this]; if (ctx['env'].config.flag) { b2 = comp1({}, key + \`__1\`, node, this, null); } @@ -1734,7 +1734,7 @@ exports[`renderings, destruction, patch, stuff, ... yet another variation 3`] = return function template(ctx, node, key = \\"\\") { const b2 = text(\`D\`); - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; const b3 = block3([hdlr1, txt1]); return multi([b2, b3]); @@ -1751,7 +1751,7 @@ exports[`renderings, destruction, patch, stuff, ... yet another variation 4`] = return function template(ctx, node, key = \\"\\") { const b2 = text(\`C\`); - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].val; const b3 = block3([hdlr1, txt1]); return multi([b2, b3]); diff --git a/tests/components/__snapshots__/event_handling.test.ts.snap b/tests/components/__snapshots__/event_handling.test.ts.snap index d111aadc..fc53fe9a 100644 --- a/tests/components/__snapshots__/event_handling.test.ts.snap +++ b/tests/components/__snapshots__/event_handling.test.ts.snap @@ -8,7 +8,7 @@ exports[`event handling Invalid handler throws an error 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['dosomething'], ctx]; + let hdlr1 = [ctx['dosomething'], this]; return block1([hdlr1]); } }" @@ -22,7 +22,7 @@ exports[`event handling handler is not called if component is destroyed 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['click'], ctx]; + let hdlr1 = [ctx['click'], this]; return block1([hdlr1]); } }" @@ -37,7 +37,7 @@ exports[`event handling handler receive the event as argument 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['inc'], ctx]; + let hdlr1 = [ctx['inc'], this]; const b2 = comp1({}, key + \`__1\`, node, this, null); let txt1 = ctx['state'].value; return block1([hdlr1, txt1], [b2]); @@ -66,7 +66,7 @@ exports[`event handling handler works when app is mounted in an iframe 1`] = ` let block1 = createBlock(\`click me\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['inc'], ctx]; + let hdlr1 = [ctx['inc'], this]; return block1([hdlr1]); } }" @@ -98,7 +98,7 @@ exports[`event handling input blur event is not called if component is destroyed let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['blur'], ctx]; + let hdlr1 = [ctx['blur'], this]; return block1([hdlr1]); } }" @@ -121,7 +121,7 @@ exports[`event handling objects from scope are properly captured by t-on 1`] = ` const key1 = ctx['item']; const v1 = ctx['onClick']; const v2 = ctx['item']; - let hdlr1 = [_ev=>v1(v2.val,_ev), ctx]; + let hdlr1 = [_ev=>v1(v2.val,_ev), this]; c_block2[i1] = withKey(block3([hdlr1]), key1); } const b2 = list(c_block2); @@ -139,7 +139,7 @@ exports[`event handling support for callable expression in event handler 1`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].value; - let hdlr1 = [ctx['obj'].onInput, ctx]; + let hdlr1 = [ctx['obj'].onInput, this]; return block1([txt1, hdlr1]); } }" @@ -162,7 +162,7 @@ exports[`event handling t-on with handler bound to dynamic argument on a t-forea const key1 = ctx['item']; const v1 = ctx['onClick']; const v2 = ctx['item']; - let hdlr1 = [_ev=>v1(v2,_ev), ctx]; + let hdlr1 = [_ev=>v1(v2,_ev), this]; c_block2[i1] = withKey(block3([hdlr1]), key1); } const b2 = list(c_block2); diff --git a/tests/components/__snapshots__/higher_order_component.test.ts.snap b/tests/components/__snapshots__/higher_order_component.test.ts.snap index dd69227b..695b2dd8 100644 --- a/tests/components/__snapshots__/higher_order_component.test.ts.snap +++ b/tests/components/__snapshots__/higher_order_component.test.ts.snap @@ -138,7 +138,7 @@ exports[`basics sub widget is interactive 2`] = ` let block1 = createBlock(\`child\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['inc'], ctx]; + let hdlr1 = [ctx['inc'], this]; let txt1 = ctx['state'].val; return block1([hdlr1, txt1]); } diff --git a/tests/components/__snapshots__/lifecycle.test.ts.snap b/tests/components/__snapshots__/lifecycle.test.ts.snap index 285e939d..a0bbcf93 100644 --- a/tests/components/__snapshots__/lifecycle.test.ts.snap +++ b/tests/components/__snapshots__/lifecycle.test.ts.snap @@ -563,7 +563,7 @@ exports[`lifecycle hooks onWillRender 2`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['increment'], ctx]; + let hdlr1 = [ctx['increment'], this]; let txt1 = ctx['state'].value; return block1([hdlr1, txt1]); } diff --git a/tests/components/__snapshots__/props.test.ts.snap b/tests/components/__snapshots__/props.test.ts.snap index 7f67df30..da0e7129 100644 --- a/tests/components/__snapshots__/props.test.ts.snap +++ b/tests/components/__snapshots__/props.test.ts.snap @@ -59,7 +59,7 @@ exports[`basics arrow functions as prop correctly capture their scope 2`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['toggle'], ctx]; + let hdlr1 = [ctx['toggle'], this]; const slot1 = (ctx['current'].slot); const b2 = toggler(slot1, callSlot(ctx, node, key + \`__1\`, slot1, true, {})); return block1([hdlr1], [b2]); @@ -752,7 +752,7 @@ exports[`slots dynamic t-slot call with default 2`] = ` } return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['toggle'], ctx]; + let hdlr1 = [ctx['toggle'], this]; const b3 = callSlot(ctx, node, key + \`__1\`, (ctx['current'].slot), true, {}, defaultContent1.bind(this)); return block1([hdlr1], [b3]); } @@ -842,7 +842,7 @@ exports[`slots mix of slots, t-call, t-call with body, and giving own props chil let block2 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['inc'], ctx]; + let hdlr1 = [ctx['inc'], this]; const b2 = block2([hdlr1]); const b3 = comp1({number: ctx['state'].number}, key + \`__1\`, node, this, null); return multi([b2, b3]); @@ -1847,7 +1847,7 @@ exports[`slots slot are properly rendered if inner props are changed 1`] = ` } return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['inc'], ctx]; + let hdlr1 = [ctx['inc'], this]; let txt1 = ctx['state'].val; const b3 = comp2({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx}})}, key + \`__2\`, node, this, null); return block1([hdlr1, txt1], [b3]); @@ -1993,7 +1993,7 @@ exports[`slots slot content is bound to caller (variation) 1`] = ` ctx[isBoundary] = 1 setContextValue(ctx, \\"var\\", 1); const v1 = ctx['this']; - let hdlr1 = [()=>v1.inc(), ctx]; + let hdlr1 = [()=>v1.inc(), this]; return block1([hdlr1]); } @@ -2029,7 +2029,7 @@ exports[`slots slot content is bound to caller 1`] = ` let block1 = createBlock(\`\`); function slot1(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['inc'], ctx]; + let hdlr1 = [ctx['inc'], this]; return block1([hdlr1]); } @@ -2361,7 +2361,7 @@ exports[`slots slots are properly bound to correct component 2`] = ` ctx[isBoundary] = 1 setContextValue(ctx, \\"var\\", 1); const v1 = ctx['this']; - let hdlr1 = [()=>v1.increment(), ctx]; + let hdlr1 = [()=>v1.increment(), this]; let txt1 = ctx['state'].value; return block1([hdlr1, txt1]); } @@ -2383,7 +2383,7 @@ exports[`slots slots are rendered with proper context 1`] = ` let block2 = createBlock(\`\`); function slot1(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['doSomething'], ctx]; + let hdlr1 = [ctx['doSomething'], this]; return block2([hdlr1]); } @@ -3046,7 +3046,7 @@ exports[`slots t-slot scope context 2`] = ` let block1 = createBlock(\`
\`); function slot1(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['onClick'], ctx]; + let hdlr1 = [ctx['onClick'], this]; const b2 = callSlot(ctx, node, key, 'default', false, {}); return block1([hdlr1], [b2]); } diff --git a/tests/components/__snapshots__/t_call.test.ts.snap b/tests/components/__snapshots__/t_call.test.ts.snap index 7cffa0ae..d5b8aab2 100644 --- a/tests/components/__snapshots__/t_call.test.ts.snap +++ b/tests/components/__snapshots__/t_call.test.ts.snap @@ -110,7 +110,7 @@ exports[`t-call handlers are properly bound through a dynamic t-call 2`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['this']; - let hdlr1 = [()=>v1.update(), ctx]; + let hdlr1 = [()=>v1.update(), this]; return block1([hdlr1]); } }" @@ -140,7 +140,7 @@ exports[`t-call handlers are properly bound through a t-call 2`] = ` let block1 = createBlock(\`

lucas

\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['update'], ctx]; + let hdlr1 = [ctx['update'], this]; return block1([hdlr1]); } }" @@ -171,7 +171,7 @@ exports[`t-call handlers with arguments are properly bound through a t-call 2`] return function template(ctx, node, key = \\"\\") { const v1 = ctx['this']; const v2 = ctx['a']; - let hdlr1 = [()=>v1.update(v2), ctx]; + let hdlr1 = [()=>v1.update(v2), this]; return block1([hdlr1]); } }" @@ -289,7 +289,7 @@ exports[`t-call recursive t-call binding this -- static t-call 2`] = ` ctx[isBoundary] = 1 let b2; if (ctx['level']<2) { - let hdlr1 = [\\"stop\\", ctx['onClicked'].bind(ctx['this']), ctx]; + let hdlr1 = [\\"stop\\", ctx['onClicked'].bind(ctx['this']), this]; let txt1 = ctx['level']; const b3 = block3([hdlr1, txt1]); ctx = Object.create(ctx); @@ -576,6 +576,33 @@ exports[`t-call t-call-context: ComponentNode is not looked up in the context 3` }" `; +exports[`t-call t-call-context: handlers have the correct this 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`someTemplate\`); + + return function template(ctx, node, key = \\"\\") { + let ctx1 = {handler:ctx['handler']}; + return callTemplate_1.call(this, ctx1, node, key + \`__1\`); + } +}" +`; + +exports[`t-call t-call-context: handlers have the correct this 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let hdlr1 = [ctx['handler'], this]; + return block1([hdlr1]); + } +}" +`; + exports[`t-call t-call-context: slots don't make component available again when context is captured 1`] = ` "function anonymous(app, bdom, helpers ) { diff --git a/tests/components/__snapshots__/t_component.test.ts.snap b/tests/components/__snapshots__/t_component.test.ts.snap index 36405cca..0f2cb22f 100644 --- a/tests/components/__snapshots__/t_component.test.ts.snap +++ b/tests/components/__snapshots__/t_component.test.ts.snap @@ -148,7 +148,7 @@ exports[`t-component modifying a sub widget 2`] = ` return function template(ctx, node, key = \\"\\") { let txt1 = ctx['state'].counter; const v1 = ctx['state']; - let hdlr1 = [()=>v1.counter++, ctx]; + let hdlr1 = [()=>v1.counter++, this]; return block1([txt1, hdlr1]); } }" diff --git a/tests/components/__snapshots__/t_model.test.ts.snap b/tests/components/__snapshots__/t_model.test.ts.snap index 411c7f65..51c69a43 100644 --- a/tests/components/__snapshots__/t_model.test.ts.snap +++ b/tests/components/__snapshots__/t_model.test.ts.snap @@ -127,7 +127,7 @@ exports[`t-model directive can also define t-on directive on same event, part 1 const expr1 = 'text'; let attr1 = bExpr1[expr1]; let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }]; - let hdlr2 = [ctx['onInput'], ctx]; + let hdlr2 = [ctx['onInput'], this]; return block1([attr1, hdlr1, hdlr2]); } }" @@ -146,17 +146,17 @@ exports[`t-model directive can also define t-on directive on same event, part 2 const expr1 = 'choice'; let attr1 = bExpr1[expr1] === 'One'; let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }]; - let hdlr2 = [ctx['onClick'], ctx]; + let hdlr2 = [ctx['onClick'], this]; const bExpr2 = ctx['state']; const expr2 = 'choice'; let attr2 = bExpr2[expr2] === 'Two'; let hdlr3 = [(ev) => { bExpr2[expr2] = ev.target.value; }]; - let hdlr4 = [ctx['onClick'], ctx]; + let hdlr4 = [ctx['onClick'], this]; const bExpr3 = ctx['state']; const expr3 = 'choice'; let attr3 = bExpr3[expr3] === 'Three'; let hdlr5 = [(ev) => { bExpr3[expr3] = ev.target.value; }]; - let hdlr6 = [ctx['onClick'], ctx]; + let hdlr6 = [ctx['onClick'], this]; return block1([attr1, hdlr1, hdlr2, attr2, hdlr3, hdlr4, attr3, hdlr5, hdlr6]); } }" @@ -423,7 +423,7 @@ exports[`t-model directive t-model is applied before t-on-input 1`] = ` const expr1 = 'text'; let attr1 = bExpr1[expr1]; let hdlr1 = [(ev) => { bExpr1[expr1] = ev.target.value; }]; - let hdlr2 = [ctx['onInput'], ctx]; + let hdlr2 = [ctx['onInput'], this]; return block1([attr1, hdlr1, hdlr2]); } }" diff --git a/tests/components/__snapshots__/t_on.test.ts.snap b/tests/components/__snapshots__/t_on.test.ts.snap index 9d710f37..c14cebc1 100644 --- a/tests/components/__snapshots__/t_on.test.ts.snap +++ b/tests/components/__snapshots__/t_on.test.ts.snap @@ -20,7 +20,7 @@ exports[`t-on t-on expression captured in t-foreach 1`] = ` const key1 = ctx['val']; const v1 = ctx['otherState']; const v2 = ctx['iter']; - let hdlr1 = [()=>v1.vals.push(v2+'_'+v2), ctx]; + let hdlr1 = [()=>v1.vals.push(v2+'_'+v2), this]; setContextValue(ctx, \\"iter\\", ctx['iter']+1); c_block2[i1] = withKey(block3([hdlr1]), key1); } @@ -50,7 +50,7 @@ exports[`t-on t-on expression in t-foreach 1`] = ` let txt2 = ctx['val']+''; const v1 = ctx['otherState']; const v2 = ctx['val']; - let hdlr1 = [()=>v1.vals.push(v2), ctx]; + let hdlr1 = [()=>v1.vals.push(v2), this]; c_block2[i1] = withKey(block3([txt1, txt2, hdlr1]), key1); } const b2 = list(c_block2); @@ -84,7 +84,7 @@ exports[`t-on t-on expression in t-foreach with t-set 1`] = ` const v1 = ctx['otherState']; const v2 = ctx['val']; const v3 = ctx['bossa']; - let hdlr1 = [()=>v1.vals.push(v2+'_'+v3), ctx]; + let hdlr1 = [()=>v1.vals.push(v2+'_'+v3), this]; c_block2[i1] = withKey(block3([txt1, txt2, hdlr1]), key1); } const b2 = list(c_block2); @@ -113,7 +113,7 @@ exports[`t-on t-on method call in t-foreach 1`] = ` let txt2 = ctx['val']+''; const v1 = ctx['this']; const v2 = ctx['val']; - let hdlr1 = [()=>v1.addVal(v2), ctx]; + let hdlr1 = [()=>v1.addVal(v2), this]; c_block2[i1] = withKey(block3([txt1, txt2, hdlr1]), key1); } const b2 = list(c_block2); @@ -133,9 +133,9 @@ exports[`t-on t-on on component next to t-on on div 1`] = ` let block1 = createBlock(\`

dec

\`); return function template(ctx, node, key = \\"\\") { - const hdlr1 = [ctx['increment'], ctx]; + const hdlr1 = [ctx['increment'], this]; const b2 = catcher1(comp1({value: ctx['state'].value}, key + \`__1\`, node, this, null), [hdlr1]); - let hdlr2 = [ctx['decrement'], ctx]; + let hdlr2 = [ctx['decrement'], this]; return block1([hdlr2], [b2]); } }" @@ -164,7 +164,7 @@ exports[`t-on t-on on components 1`] = ` const catcher1 = createCatcher({\\"click\\":0}); return function template(ctx, node, key = \\"\\") { - const hdlr1 = [ctx['increment'], ctx]; + const hdlr1 = [ctx['increment'], this]; return catcher1(comp1({value: ctx['state'].value}, key + \`__1\`, node, this, null), [hdlr1]); } }" @@ -200,7 +200,7 @@ exports[`t-on t-on on components and t-foreach 1`] = ` const key1 = ctx['name']; const v1 = ctx['this']; const v2 = ctx['name']; - const hdlr1 = [()=>v1.log(v2), ctx]; + const hdlr1 = [()=>v1.log(v2), this]; c_block1[i1] = withKey(catcher1(comp1({value: ctx['name']}, key + \`__1__\${key1}\`, node, this, null), [hdlr1]), key1); } return list(c_block1); @@ -233,7 +233,7 @@ exports[`t-on t-on on components, variation 1`] = ` let block1 = createBlock(\`

\`); return function template(ctx, node, key = \\"\\") { - const hdlr1 = [ctx['increment'], ctx]; + const hdlr1 = [ctx['increment'], this]; const b2 = catcher1(comp1({value: ctx['state'].value}, key + \`__1\`, node, this, null), [hdlr1]); return block1([], [b2]); } @@ -263,7 +263,7 @@ exports[`t-on t-on on components, with 'prevent' modifier 1`] = ` const catcher1 = createCatcher({\\"click.prevent\\":0}); return function template(ctx, node, key = \\"\\") { - const hdlr1 = [\\"prevent\\", ctx['increment'], ctx]; + const hdlr1 = [\\"prevent\\", ctx['increment'], this]; return catcher1(comp1({value: ctx['state'].value}, key + \`__1\`, node, this, null), [hdlr1]); } }" @@ -297,7 +297,7 @@ exports[`t-on t-on on components, with a handler update 1`] = ` setContextValue(ctx, \\"name\\", ctx['state'].name); const v1 = ctx['this']; const v2 = ctx['name']; - const hdlr1 = [()=>v1.log(v2), ctx]; + const hdlr1 = [()=>v1.log(v2), this]; return catcher1(comp1({value: ctx['name']}, key + \`__1\`, node, this, null), [hdlr1]); } }" @@ -343,7 +343,7 @@ exports[`t-on t-on on destroyed components 2`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['onClick'], ctx]; + let hdlr1 = [ctx['onClick'], this]; return block1([hdlr1]); } }" @@ -376,7 +376,7 @@ exports[`t-on t-on on slot, with 'prevent' modifier 2`] = ` const catcher1 = createCatcher({\\"click.prevent\\":0}); return function template(ctx, node, key = \\"\\") { - const hdlr1 = [\\"prevent\\", ctx['doSomething'], ctx]; + const hdlr1 = [\\"prevent\\", ctx['doSomething'], this]; return catcher1(callSlot(ctx, node, key, 'default', false, {}), [hdlr1]); } }" @@ -397,7 +397,7 @@ exports[`t-on t-on on t-set-slots 1`] = ` const b6 = block6(); const b7 = block7(); const v1 = ctx['this']; - const hdlr1 = [()=>v1.state.count++, ctx]; + const hdlr1 = [()=>v1.state.count++, this]; return catcher1(multi([b6, b7]), [hdlr1]); } @@ -455,7 +455,7 @@ exports[`t-on t-on on t-slots 2`] = ` const b3 = text(ctx['state'].count); const b4 = text(\`] \`); const v1 = ctx['this']; - const hdlr1 = [()=>v1.state.count++, ctx]; + const hdlr1 = [()=>v1.state.count++, this]; const b5 = catcher1(callSlot(ctx, node, key, 'default', false, {}), [hdlr1]); return multi([b2, b3, b4, b5]); } @@ -473,8 +473,8 @@ exports[`t-on t-on when first component child is an empty component 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['push'], ctx]; - const hdlr2 = [()=>{}, ctx]; + let hdlr1 = [ctx['push'], this]; + const hdlr2 = [()=>{}, this]; const b2 = catcher1(comp1({list: ctx['list']}, key + \`__1\`, node, this, null), [hdlr2]); return block1([hdlr1], [b2]); } diff --git a/tests/components/t_call.test.ts b/tests/components/t_call.test.ts index 3e70dd6e..43591eab 100644 --- a/tests/components/t_call.test.ts +++ b/tests/components/t_call.test.ts @@ -398,4 +398,30 @@ describe("t-call", () => { }); expect(fixture.innerHTML).toBe(""); }); + + test("t-call-context: handlers have the correct this", async () => { + class Root extends Component { + static template = xml``; + setup() { + root = this; + } + handler() { + handlerThis = this; + } + } + let root: Root | null = null; + let handlerThis: Root | null = null; + + await mount(Root, fixture, { + templates: ` + + +
+ + `, + }); + const toClick: HTMLDivElement = fixture.querySelector(".click_me")!; + toClick.click(); + expect(handlerThis).toBe(root); + }); }); diff --git a/tests/misc/__snapshots__/portal.test.ts.snap b/tests/misc/__snapshots__/portal.test.ts.snap index a920d8da..aacd3f5e 100644 --- a/tests/misc/__snapshots__/portal.test.ts.snap +++ b/tests/misc/__snapshots__/portal.test.ts.snap @@ -240,7 +240,7 @@ exports[`Portal Portal composed with t-slot 3`] = ` let block1 = createBlock(\`
child2
\`); return function template(ctx, node, key = \\"\\") { - let hdlr1 = [ctx['onCustom'], ctx]; + let hdlr1 = [ctx['onCustom'], this]; return block1([hdlr1]); } }"