diff --git a/web/static/src/ts/core/Widget.ts b/web/static/src/ts/core/Widget.ts index 01ef8d5d..abb8de03 100644 --- a/web/static/src/ts/core/Widget.ts +++ b/web/static/src/ts/core/Widget.ts @@ -3,6 +3,7 @@ import sdListeners from "../../../libs/snabbdom/src/modules/eventlisteners"; import { init } from "../../../libs/snabbdom/src/snabbdom"; import { VNode } from "../../../libs/snabbdom/src/vnode"; import { QWeb } from "./qweb_vdom"; +import { EventBus } from "./event_bus"; //------------------------------------------------------------------------------ // Types/helpers @@ -39,7 +40,7 @@ export interface Type extends Function { // Widget //------------------------------------------------------------------------------ -export class Widget { +export class Widget extends EventBus { __widget__: Meta; template: string = "default"; @@ -57,6 +58,7 @@ export class Widget { //-------------------------------------------------------------------------- constructor(parent: Widget | T, props?: Props) { + super(); wl.push(this); // is this a good idea? // Pro: if props is empty, we can create easily a widget @@ -154,6 +156,7 @@ export class Widget { delete this.__widget__.parent.__widget__.children[id]; this.__widget__.parent = null; } + this.clear(); this.__widget__.isDestroyed = true; this.destroyed(); } diff --git a/web/static/src/ts/core/event_bus.ts b/web/static/src/ts/core/event_bus.ts index ceac65a3..23b903ad 100644 --- a/web/static/src/ts/core/event_bus.ts +++ b/web/static/src/ts/core/event_bus.ts @@ -64,4 +64,8 @@ export class EventBus { sub.callback.call(sub.owner, ...args); } } + + clear() { + this.subscriptions = {}; + } } diff --git a/web/static/src/ts/core/qweb_vdom.ts b/web/static/src/ts/core/qweb_vdom.ts index ed34f334..18fd4da6 100644 --- a/web/static/src/ts/core/qweb_vdom.ts +++ b/web/static/src/ts/core/qweb_vdom.ts @@ -733,6 +733,17 @@ const widgetDirective: Directive = { ctx.rootContext.shouldDefineOwner = true; let props = node.getAttribute("t-props"); let keepAlive = node.getAttribute("t-keep-alive") ? true : false; + + // t-on- events... + const events: [string, string][] = []; + const attributes = (node).attributes; + for (let i = 0; i < attributes.length; i++) { + const name = attributes[i].name; + if (name.startsWith("t-on-")) { + events.push([name.slice(5), attributes[i].textContent!]); + } + } + let key = node.getAttribute("t-key"); if (key) { key = qweb._formatExpression(key); @@ -792,6 +803,10 @@ const widgetDirective: Directive = { ctx.addLine( `context.__widget__.cmap[${templateID}] = _${widgetID}.__widget__.id;` ); + for (let [event, method] of events) { + ctx.addLine(`_${widgetID}.on('${event}', owner, owner['${method}'])`); + } + ctx.addLine( `def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${ ctx.parentNode diff --git a/web/static/src/ts/widgets/Navbar.ts b/web/static/src/ts/widgets/Navbar.ts index 377605b6..80e343f7 100644 --- a/web/static/src/ts/widgets/Navbar.ts +++ b/web/static/src/ts/widgets/Navbar.ts @@ -2,8 +2,7 @@ import { Widget } from "../core/widget"; import { Env, Menu } from "../env"; export interface Props { - toggleHome: () => void; - inMenu: boolean; + inHome: boolean; } export class Navbar extends Widget { @@ -16,6 +15,6 @@ export class Navbar extends Widget { toggleHome(ev: MouseEvent) { ev.preventDefault(); - this.props.toggleHome(); + this.trigger("toggle-home-menu"); } } diff --git a/web/static/src/ts/widgets/root.ts b/web/static/src/ts/widgets/root.ts index 684e2ac5..9dfd5d93 100644 --- a/web/static/src/ts/widgets/root.ts +++ b/web/static/src/ts/widgets/root.ts @@ -14,7 +14,7 @@ import { Notification } from "./notification"; interface State { notifications: INotification[]; stack: ActionStack; - inMenu: boolean; + inHome: boolean; } //------------------------------------------------------------------------------ @@ -28,7 +28,7 @@ export class Root extends Widget { state: State = { notifications: [], stack: [], - inMenu: false + inHome: false }; constructor(env: Env) { @@ -47,6 +47,6 @@ export class Root extends Widget { } toggleHome() { - this.updateState({ inMenu: !this.state.inMenu }); + this.updateState({ inHome: !this.state.inHome }); } } diff --git a/web/static/tests/core/widget.test.ts b/web/static/tests/core/widget.test.ts index a41dc149..e99c8eec 100644 --- a/web/static/tests/core/widget.test.ts +++ b/web/static/tests/core/widget.test.ts @@ -794,7 +794,35 @@ describe("props evaluation (with t-props directive)", () => { }); }); -describe("random stuff", () => { +describe("t-on directive on widgets", () => { + test("t-on works as expected", async () => { + let n = 0; + env.qweb.addTemplate( + "parent", + `
` + ); + class ParentWidget extends Widget { + template = "parent"; + widgets = { child: Child }; + someMethod(arg) { + expect(arg).toBe(43); + n++; + } + } + class Child extends Widget {} + const widget = new ParentWidget(env); + await widget.mount(fixture); + let child = children(widget)[0]; + expect(n).toBe(0); + child.trigger("customevent", 43); + expect(n).toBe(1); + child.destroy(); + child.trigger("customevent", 43); + expect(n).toBe(1); + }); +}); + +describe("random stuff/miscellaneous", () => { test("widget after a t-foreach", async () => { // this test makes sure that the foreach directive does not pollute sub // context with the inLoop variable, which is then used in the t-widget @@ -811,9 +839,7 @@ describe("random stuff", () => { await widget.mount(fixture); expect(fixture.innerHTML).toBe("
txttxt
"); }); -}); -describe("miscellaneous", () => { test("updating widget immediately", async () => { // in this situation, we protect against a bug that occurred: because of the // interplay between widgets and vnodes, a sub widget vnode was patched