From a0bfbf6dd056999213da35bb20c6eea71fcb08ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 4 Dec 2019 21:29:34 +0100 Subject: [PATCH] [FIX] qweb/component: do not call handlers for unmounted components Unmounted components should be considered inactive, at least from the perspective of Owl itself. closes #543 --- src/component/directive.ts | 2 +- src/qweb/extensions.ts | 2 +- .../__snapshots__/component.test.ts.snap | 28 +++--- tests/component/component.test.ts | 96 ++++++++++++++++++- tests/helpers.ts | 5 + tests/qweb/__snapshots__/qweb.test.ts.snap | 44 ++++----- tests/qweb/qweb.test.ts | 4 +- tests/router/__snapshots__/link.test.ts.snap | 2 +- 8 files changed, 141 insertions(+), 42 deletions(-) diff --git a/src/component/directive.ts b/src/component/directive.ts index b7039292..6e149f05 100644 --- a/src/component/directive.ts +++ b/src/component/directive.ts @@ -298,7 +298,7 @@ QWeb.addDirective({ params = `owner, ${ctx.formatExpression(extraArgs)}`; } } - let handler = `function (e) {`; + let handler = `function (e) {if(!owner.__owl__.isMounted){return}`; handler += mods .map(function(mod) { return T_COMPONENT_MODS_CODE[mod]; diff --git a/src/qweb/extensions.ts b/src/qweb/extensions.ts index 8504150a..7b99ff66 100644 --- a/src/qweb/extensions.ts +++ b/src/qweb/extensions.ts @@ -41,7 +41,7 @@ QWeb.addDirective({ return ""; }); let params = extraArgs ? `owner, ${ctx.formatExpression(extraArgs)}` : "owner"; - let handler = `function (e) {`; + let handler = `function (e) {if (!context.__owl__.isMounted){return}`; handler += mods .map(function(mod) { return MODS_CODE[mod]; diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index a0be6107..ffb36f47 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -486,7 +486,7 @@ exports[`other directives with t-component t-on with getter as handler 1`] = ` if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} w3 = new W3(parent, props3); parent.__owl__.cmap[k4] = w3.__owl__.id; - let fiber = w3.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['handler'];if (fn) { fn.call(owner, e); } else { owner.handler; }});}};}); + let fiber = w3.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['handler'];if (fn) { fn.call(owner, e); } else { owner.handler; }});}};}); let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; @@ -524,7 +524,7 @@ exports[`other directives with t-component t-on with handler bound to argument 1 if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, 3, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv'];if (fn) { fn.call(owner, 3, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -562,7 +562,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -600,7 +600,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -638,7 +638,7 @@ exports[`other directives with t-component t-on with handler bound to object 1`] if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {val:3}, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv'];if (fn) { fn.call(owner, {val:3}, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -680,7 +680,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = ` if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')} w3 = new W3(parent, props3); parent.__owl__.cmap[k4] = w3.__owl__.id; - let fiber = w3.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['state.counter++'];if (fn) { fn.call(owner, e); } else { owner.state.counter++; }});}};}); + let fiber = w3.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['state.counter++'];if (fn) { fn.call(owner, e); } else { owner.state.counter++; }});}};}); let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}}); c1.push(pvnode); w3.__owl__.pvnode = pvnode; @@ -718,7 +718,7 @@ exports[`other directives with t-component t-on with no handler (only modifiers) if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -756,7 +756,7 @@ exports[`other directives with t-component t-on with prevent and self modifiers if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {e.preventDefault();if (e.target !== vn.elm) {return}const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}e.preventDefault();if (e.target !== vn.elm) {return}const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -794,7 +794,7 @@ exports[`other directives with t-component t-on with self and prevent modifiers if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (e.target !== vn.elm) {return}e.preventDefault();const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}if (e.target !== vn.elm) {return}e.preventDefault();const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -832,7 +832,7 @@ exports[`other directives with t-component t-on with self modifier 1`] = ` if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {if (e.target !== vn.elm) {return}const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {if(!owner.__owl__.isMounted){return}if (e.target !== vn.elm) {return}const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -870,7 +870,7 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')} w2 = new W2(parent, props2); parent.__owl__.cmap[k3] = w2.__owl__.id; - let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {e.stopPropagation();const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {e.preventDefault();const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});vn.elm.addEventListener('ev-3', function (e) {e.stopPropagation();e.preventDefault();const fn = owner['onEv3'];if (fn) { fn.call(owner, e); } else { owner.onEv3; }});}};}); + let fiber = w2.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {if(!owner.__owl__.isMounted){return}e.stopPropagation();const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {if(!owner.__owl__.isMounted){return}e.preventDefault();const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});vn.elm.addEventListener('ev-3', function (e) {if(!owner.__owl__.isMounted){return}e.stopPropagation();e.preventDefault();const fn = owner['onEv3'];if (fn) { fn.call(owner, e); } else { owner.onEv3; }});}};}); let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}}); c1.push(pvnode); w2.__owl__.pvnode = pvnode; @@ -981,7 +981,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument if (!W6) {throw new Error('Cannot find the definition of component \\"' + componentKey6 + '\\"')} w6 = new W6(parent, props6); parent.__owl__.cmap[k7] = w6.__owl__.id; - let fiber = w6.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, arg9, e); } else { owner.onEv; }});}};}); + let fiber = w6.__prepare(extra.fiber, undefined, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if(!owner.__owl__.isMounted){return}const fn = owner['onEv'];if (fn) { fn.call(owner, arg9, e); } else { owner.onEv; }});}};}); let pvnode = h('dummy', {key: k7, hook: {remove() {},destroy(vn) {w6.destroy();}}}); c1.push(pvnode); w6.__owl__.pvnode = pvnode; @@ -1400,7 +1400,7 @@ exports[`t-slot directive refs are properly bound in slots 1`] = ` let c9 = [], p9 = {key:9,on:{}}; var vn9 = h('button', p9, c9); c1.push(vn9); - extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; + extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; p9.on['click'] = extra.handlers['click' + 9]; const ref10 = \`myButton\`; p9.hook = { @@ -1425,7 +1425,7 @@ exports[`t-slot directive slots are rendered with proper context 1`] = ` let c9 = [], p9 = {key:9,on:{}}; var vn9 = h('button', p9, c9); c1.push(vn9); - extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; + extra.handlers['click' + 9] = extra.handlers['click' + 9] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['doSomething'];if (fn) { fn.call(owner, e); } else { context.doSomething; }}; p9.on['click'] = extra.handlers['click' + 9]; c9.push({text: \`do something\`}); }" diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 309becfa..dde9b013 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -164,7 +164,7 @@ describe("basic widget properties", () => { button.click(); await nextTick(); expect(target.innerHTML).toBe("
0
"); - expect(counter.state.counter).toBe(1); + expect(counter.state.counter).toBe(0); }); test("widget style and classname", async () => { @@ -2234,6 +2234,100 @@ describe("other directives with t-component", () => { expect(steps).toEqual(["GrandChild", "Child", "Parent"]); }); + test("t-on on unmounted components", async () => { + const steps: string[] = []; + let child; + class Child extends Component { + static template = xml`
`; + mounted() { + child = this; + } + onClick() { + steps.push("click"); + } + } + class Parent extends Component { + static template = xml`
`; + static components = { Child }; + state = useState({ flag: true }); + } + const parent = new Parent(); + await parent.mount(fixture); + + let el = child.el; + el.click(); + expect(steps).toEqual(["click"]); + parent.unmount(); + expect(child.__owl__.isMounted).toBe(false); + el.click(); + expect(steps).toEqual(["click"]); + }); + + test("t-on on destroyed components", async () => { + const steps: string[] = []; + let child; + class Child extends Component { + static template = xml`
`; + mounted() { + child = this; + } + onClick() { + steps.push("click"); + } + } + class Parent extends Component { + static template = xml`
`; + static components = { Child }; + state = useState({ flag: true }); + } + const parent = new Parent(); + await parent.mount(fixture); + + let el = child.el; + el.click(); + expect(steps).toEqual(["click"]); + parent.state.flag = false; + await nextTick(); + expect(child.__owl__.isDestroyed).toBe(true); + el.click(); + expect(steps).toEqual(["click"]); + }); + + test("t-on on destroyed components, part 2", async () => { + const steps: string[] = []; + let child; + class GrandChild extends Component { + static template = xml`
grandchild
`; + } + class Child extends Component { + static template = xml`
`; + static components = { GrandChild }; + gc = useRef("gc"); + mounted() { + child = this; + } + onClick() { + steps.push("click"); + } + } + class Parent extends Component { + static template = xml`
`; + static components = { Child }; + state = useState({ flag: true }); + } + const parent = new Parent(); + await parent.mount(fixture); + + let el = child.gc.el; + el.click(); + expect(steps).toEqual(["click"]); + parent.state.flag = false; + await nextTick(); + expect(child.__owl__.isDestroyed).toBe(true); + el.click(); + expect(steps).toEqual(["click"]); + }); + test("t-if works with t-component", async () => { env.qweb.addTemplate("ParentWidget", `
`); class Child extends Widget {} diff --git a/tests/helpers.ts b/tests/helpers.ts index c8f7bdbd..23e51799 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -87,6 +87,11 @@ export function renderToDOM( context: EvalContext = {}, extra?: any ): HTMLElement | Text { + if (!context.__owl__) { + // we add `__owl__` to better simulate a component as context. This is + // particularly important for event handlers added with the `t-on` directive. + context.__owl__ = { isMounted: true }; + } const vnode = qweb.render(template, context, extra); // we snapshot here the compiled code. This is useful to prevent unwanted code diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap index a9f813b6..157db223 100644 --- a/tests/qweb/__snapshots__/qweb.test.ts.snap +++ b/tests/qweb/__snapshots__/qweb.test.ts.snap @@ -1705,7 +1705,7 @@ exports[`t-on can bind event handler 1`] = ` var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {const fn = context['add'];if (fn) { fn.call(owner, e); } else { context.add; }}; + extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['add'];if (fn) { fn.call(owner, e); } else { context.add; }}; p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; @@ -1720,7 +1720,7 @@ exports[`t-on can bind handlers with arguments 1`] = ` let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); const handler2 = context['add'] && context['add'].bind(owner, 5); - p1.on['click'] = function (e) {if (handler2) { handler2(e); } else { context.add(5); }}; + p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}if (handler2) { handler2(e); } else { context.add(5); }}; c1.push({text: \`Click\`}); return vn1; }" @@ -1734,7 +1734,7 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); const handler2 = context['doSomething'] && context['doSomething'].bind(owner, {}); - p1.on['click'] = function (e) {if (handler2) { handler2(e); } else { context.doSomething({ }); }}; + p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}if (handler2) { handler2(e); } else { context.doSomething({ }); }}; c1.push({text: \`Click\`}); return vn1; }" @@ -1748,7 +1748,7 @@ exports[`t-on can bind handlers with empty object 1`] = ` let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); const handler2 = context['doSomething'] && context['doSomething'].bind(owner, {}); - p1.on['click'] = function (e) {if (handler2) { handler2(e); } else { context.doSomething({}); }}; + p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}if (handler2) { handler2(e); } else { context.doSomething({}); }}; c1.push({text: \`Click\`}); return vn1; }" @@ -1784,7 +1784,7 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = ` var vn7 = h('a', p7, c7); c6.push(vn7); const handler8 = context['activate'] && context['activate'].bind(owner, context['action']); - p7.on['click'] = function (e) {if (handler8) { handler8(e); } else { context.activate(action); }}; + p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}if (handler8) { handler8(e); } else { context.activate(action); }}; c7.push({text: \`link\`}); } return vn1; @@ -1799,7 +1799,7 @@ exports[`t-on can bind handlers with object arguments 1`] = ` let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); const handler2 = context['add'] && context['add'].bind(owner, {val:5}); - p1.on['click'] = function (e) {if (handler2) { handler2(e); } else { context.add({val: 5}); }}; + p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}if (handler2) { handler2(e); } else { context.add({val: 5}); }}; c1.push({text: \`Click\`}); return vn1; }" @@ -1812,9 +1812,9 @@ exports[`t-on can bind two event handlers 1`] = ` var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {const fn = context['handleClick'];if (fn) { fn.call(owner, e); } else { context.handleClick; }}; + extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['handleClick'];if (fn) { fn.call(owner, e); } else { context.handleClick; }}; p1.on['click'] = extra.handlers['click' + 1]; - extra.handlers['dblclick' + 1] = extra.handlers['dblclick' + 1] || function (e) {const fn = context['handleDblClick'];if (fn) { fn.call(owner, e); } else { context.handleDblClick; }}; + extra.handlers['dblclick' + 1] = extra.handlers['dblclick' + 1] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['handleDblClick'];if (fn) { fn.call(owner, e); } else { context.handleDblClick; }}; p1.on['dblclick'] = extra.handlers['dblclick' + 1]; c1.push({text: \`Click\`}); return vn1; @@ -1828,7 +1828,7 @@ exports[`t-on handler is bound to proper owner 1`] = ` var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {const fn = context['add'];if (fn) { fn.call(owner, e); } else { context.add; }}; + extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['add'];if (fn) { fn.call(owner, e); } else { context.add; }}; p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; @@ -1845,7 +1845,7 @@ exports[`t-on t-on combined with t-esc 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; + extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; p2.on['click'] = extra.handlers['click' + 2]; var _3 = context['text']; if (_3 || _3 === 0) { @@ -1866,7 +1866,7 @@ exports[`t-on t-on combined with t-raw 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; + extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; p2.on['click'] = extra.handlers['click' + 2]; var _3 = context['html']; if (_3 || _3 === 0) { @@ -1886,7 +1886,7 @@ exports[`t-on t-on with empty handler (only modifiers) 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - p2.on['click'] = function (e) {e.preventDefault();}; + p2.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();}; c2.push({text: \`Button\`}); return vn1; }" @@ -1900,7 +1900,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = ` let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); const handler2 = context['state.incrementCounter'] && context['state.incrementCounter'].bind(owner, 2); - p1.on['click'] = function (e) {if (handler2) { handler2(e); } else { context.state.incrementCounter(2); }}; + p1.on['click'] = function (e) {if (!context.__owl__.isMounted){return}if (handler2) { handler2(e); } else { context.state.incrementCounter(2); }}; c1.push({text: \`Click\`}); return vn1; }" @@ -1913,7 +1913,7 @@ exports[`t-on t-on with inline statement 1`] = ` var h = this.h; let c1 = [], p1 = {key:1,on:{}}; var vn1 = h('button', p1, c1); - extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {const fn = context['state.counter++'];if (fn) { fn.call(owner, e); } else { context.state.counter++; }}; + extra.handlers['click' + 1] = extra.handlers['click' + 1] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['state.counter++'];if (fn) { fn.call(owner, e); } else { context.state.counter++; }}; p1.on['click'] = extra.handlers['click' + 1]; c1.push({text: \`Click\`}); return vn1; @@ -1930,7 +1930,7 @@ exports[`t-on t-on with prevent and self modifiers (order matters) 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {e.preventDefault();if (e.target !== this.elm) {return}const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; + extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();if (e.target !== this.elm) {return}const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; p2.on['click'] = extra.handlers['click' + 2]; let c3 = [], p3 = {key:3}; var vn3 = h('span', p3, c3); @@ -1950,19 +1950,19 @@ exports[`t-on t-on with prevent and/or stop modifiers 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {e.preventDefault();const fn = context['onClickPrevented'];if (fn) { fn.call(owner, e); } else { context.onClickPrevented; }}; + extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();const fn = context['onClickPrevented'];if (fn) { fn.call(owner, e); } else { context.onClickPrevented; }}; p2.on['click'] = extra.handlers['click' + 2]; c2.push({text: \`Button 1\`}); let c3 = [], p3 = {key:3,on:{}}; var vn3 = h('button', p3, c3); c1.push(vn3); - extra.handlers['click' + 3] = extra.handlers['click' + 3] || function (e) {e.stopPropagation();const fn = context['onClickStopped'];if (fn) { fn.call(owner, e); } else { context.onClickStopped; }}; + extra.handlers['click' + 3] = extra.handlers['click' + 3] || function (e) {if (!context.__owl__.isMounted){return}e.stopPropagation();const fn = context['onClickStopped'];if (fn) { fn.call(owner, e); } else { context.onClickStopped; }}; p3.on['click'] = extra.handlers['click' + 3]; c3.push({text: \`Button 2\`}); let c4 = [], p4 = {key:4,on:{}}; var vn4 = h('button', p4, c4); c1.push(vn4); - extra.handlers['click' + 4] = extra.handlers['click' + 4] || function (e) {e.preventDefault();e.stopPropagation();const fn = context['onClickPreventedAndStopped'];if (fn) { fn.call(owner, e); } else { context.onClickPreventedAndStopped; }}; + extra.handlers['click' + 4] = extra.handlers['click' + 4] || function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();e.stopPropagation();const fn = context['onClickPreventedAndStopped'];if (fn) { fn.call(owner, e); } else { context.onClickPreventedAndStopped; }}; p4.on['click'] = extra.handlers['click' + 4]; c4.push({text: \`Button 3\`}); return vn1; @@ -1997,7 +1997,7 @@ exports[`t-on t-on with prevent modifier in t-foreach 1`] = ` var vn7 = h('a', p7, c7); c1.push(vn7); const handler8 = context['onEdit'] && context['onEdit'].bind(owner, context['project'].id); - p7.on['click'] = function (e) {e.preventDefault();if (handler8) { handler8(e); } else { context.onEdit(project.id); }}; + p7.on['click'] = function (e) {if (!context.__owl__.isMounted){return}e.preventDefault();if (handler8) { handler8(e); } else { context.onEdit(project.id); }}; c7.push({text: \` Edit \`}); var _9 = context['project'].name; if (_9 || _9 === 0) { @@ -2018,7 +2018,7 @@ exports[`t-on t-on with self and prevent modifiers (order matters) 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (e.target !== this.elm) {return}e.preventDefault();const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; + extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (!context.__owl__.isMounted){return}if (e.target !== this.elm) {return}e.preventDefault();const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; p2.on['click'] = extra.handlers['click' + 2]; let c3 = [], p3 = {key:3}; var vn3 = h('span', p3, c3); @@ -2038,7 +2038,7 @@ exports[`t-on t-on with self modifier 1`] = ` let c2 = [], p2 = {key:2,on:{}}; var vn2 = h('button', p2, c2); c1.push(vn2); - extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; + extra.handlers['click' + 2] = extra.handlers['click' + 2] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['onClick'];if (fn) { fn.call(owner, e); } else { context.onClick; }}; p2.on['click'] = extra.handlers['click' + 2]; let c3 = [], p3 = {key:3}; var vn3 = h('span', p3, c3); @@ -2047,7 +2047,7 @@ exports[`t-on t-on with self modifier 1`] = ` let c4 = [], p4 = {key:4,on:{}}; var vn4 = h('button', p4, c4); c1.push(vn4); - extra.handlers['click' + 4] = extra.handlers['click' + 4] || function (e) {if (e.target !== this.elm) {return}const fn = context['onClickSelf'];if (fn) { fn.call(owner, e); } else { context.onClickSelf; }}; + extra.handlers['click' + 4] = extra.handlers['click' + 4] || function (e) {if (!context.__owl__.isMounted){return}if (e.target !== this.elm) {return}const fn = context['onClickSelf'];if (fn) { fn.call(owner, e); } else { context.onClickSelf; }}; p4.on['click'] = extra.handlers['click' + 4]; let c5 = [], p5 = {key:5}; var vn5 = h('span', p5, c5); diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts index 91ee0495..e637a636 100644 --- a/tests/qweb/qweb.test.ts +++ b/tests/qweb/qweb.test.ts @@ -875,9 +875,9 @@ describe("foreach", () => {
` ); - const context = {}; + const context = { __owl__: {} }; renderToString(qweb, "test", context); - expect(Object.keys(context).length).toBe(0); + expect(Object.keys(context)).toEqual(["__owl__"]); }); test("t-foreach in t-forach", () => { diff --git a/tests/router/__snapshots__/link.test.ts.snap b/tests/router/__snapshots__/link.test.ts.snap index 5c351c23..e8c4660c 100644 --- a/tests/router/__snapshots__/link.test.ts.snap +++ b/tests/router/__snapshots__/link.test.ts.snap @@ -10,7 +10,7 @@ exports[`Link component can render simple cases 1`] = ` var _6 = context['href']; let c7 = [], p7 = {key:7,attrs:{href: _6},class:_5,on:{}}; var vn7 = h('a', p7, c7); - extra.handlers['click' + 7] = extra.handlers['click' + 7] || function (e) {const fn = context['navigate'];if (fn) { fn.call(owner, e); } else { context.navigate; }}; + extra.handlers['click' + 7] = extra.handlers['click' + 7] || function (e) {if (!context.__owl__.isMounted){return}const fn = context['navigate'];if (fn) { fn.call(owner, e); } else { context.navigate; }}; p7.on['click'] = extra.handlers['click' + 7]; const slot8 = this.constructor.slots[context.__owl__.slotId + '_' + 'default']; if (slot8) {