widget is now a bus, add t-on directive on widgets

This commit is contained in:
Géry Debongnie
2019-02-02 18:17:21 +01:00
parent 0e1e0da3e7
commit fdd11907fb
6 changed files with 57 additions and 10 deletions
+4 -1
View File
@@ -3,6 +3,7 @@ import sdListeners from "../../../libs/snabbdom/src/modules/eventlisteners";
import { init } from "../../../libs/snabbdom/src/snabbdom"; import { init } from "../../../libs/snabbdom/src/snabbdom";
import { VNode } from "../../../libs/snabbdom/src/vnode"; import { VNode } from "../../../libs/snabbdom/src/vnode";
import { QWeb } from "./qweb_vdom"; import { QWeb } from "./qweb_vdom";
import { EventBus } from "./event_bus";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Types/helpers // Types/helpers
@@ -39,7 +40,7 @@ export interface Type<T> extends Function {
// Widget // Widget
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
export class Widget<T extends WEnv, Props> { export class Widget<T extends WEnv, Props> extends EventBus {
__widget__: Meta<WEnv>; __widget__: Meta<WEnv>;
template: string = "default"; template: string = "default";
@@ -57,6 +58,7 @@ export class Widget<T extends WEnv, Props> {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
constructor(parent: Widget<T, any> | T, props?: Props) { constructor(parent: Widget<T, any> | T, props?: Props) {
super();
wl.push(this); wl.push(this);
// is this a good idea? // is this a good idea?
// Pro: if props is empty, we can create easily a widget // Pro: if props is empty, we can create easily a widget
@@ -154,6 +156,7 @@ export class Widget<T extends WEnv, Props> {
delete this.__widget__.parent.__widget__.children[id]; delete this.__widget__.parent.__widget__.children[id];
this.__widget__.parent = null; this.__widget__.parent = null;
} }
this.clear();
this.__widget__.isDestroyed = true; this.__widget__.isDestroyed = true;
this.destroyed(); this.destroyed();
} }
+4
View File
@@ -64,4 +64,8 @@ export class EventBus {
sub.callback.call(sub.owner, ...args); sub.callback.call(sub.owner, ...args);
} }
} }
clear() {
this.subscriptions = {};
}
} }
+15
View File
@@ -733,6 +733,17 @@ const widgetDirective: Directive = {
ctx.rootContext.shouldDefineOwner = true; ctx.rootContext.shouldDefineOwner = true;
let props = node.getAttribute("t-props"); let props = node.getAttribute("t-props");
let keepAlive = node.getAttribute("t-keep-alive") ? true : false; let keepAlive = node.getAttribute("t-keep-alive") ? true : false;
// t-on- events...
const events: [string, string][] = [];
const attributes = (<Element>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"); let key = node.getAttribute("t-key");
if (key) { if (key) {
key = qweb._formatExpression(key); key = qweb._formatExpression(key);
@@ -792,6 +803,10 @@ const widgetDirective: Directive = {
ctx.addLine( ctx.addLine(
`context.__widget__.cmap[${templateID}] = _${widgetID}.__widget__.id;` `context.__widget__.cmap[${templateID}] = _${widgetID}.__widget__.id;`
); );
for (let [event, method] of events) {
ctx.addLine(`_${widgetID}.on('${event}', owner, owner['${method}'])`);
}
ctx.addLine( ctx.addLine(
`def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${ `def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${
ctx.parentNode ctx.parentNode
+2 -3
View File
@@ -2,8 +2,7 @@ import { Widget } from "../core/widget";
import { Env, Menu } from "../env"; import { Env, Menu } from "../env";
export interface Props { export interface Props {
toggleHome: () => void; inHome: boolean;
inMenu: boolean;
} }
export class Navbar extends Widget<Env, Props> { export class Navbar extends Widget<Env, Props> {
@@ -16,6 +15,6 @@ export class Navbar extends Widget<Env, Props> {
toggleHome(ev: MouseEvent) { toggleHome(ev: MouseEvent) {
ev.preventDefault(); ev.preventDefault();
this.props.toggleHome(); this.trigger("toggle-home-menu");
} }
} }
+3 -3
View File
@@ -14,7 +14,7 @@ import { Notification } from "./notification";
interface State { interface State {
notifications: INotification[]; notifications: INotification[];
stack: ActionStack; stack: ActionStack;
inMenu: boolean; inHome: boolean;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -28,7 +28,7 @@ export class Root extends Widget<Env, {}> {
state: State = { state: State = {
notifications: [], notifications: [],
stack: [], stack: [],
inMenu: false inHome: false
}; };
constructor(env: Env) { constructor(env: Env) {
@@ -47,6 +47,6 @@ export class Root extends Widget<Env, {}> {
} }
toggleHome() { toggleHome() {
this.updateState({ inMenu: !this.state.inMenu }); this.updateState({ inHome: !this.state.inHome });
} }
} }
+29 -3
View File
@@ -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",
`<div><t t-widget="child" t-on-customevent="someMethod"/></div>`
);
class ParentWidget extends Widget<WEnv, {}> {
template = "parent";
widgets = { child: Child };
someMethod(arg) {
expect(arg).toBe(43);
n++;
}
}
class Child extends Widget<WEnv, {}> {}
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 () => { test("widget after a t-foreach", async () => {
// this test makes sure that the foreach directive does not pollute sub // 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 // context with the inLoop variable, which is then used in the t-widget
@@ -811,9 +839,7 @@ describe("random stuff", () => {
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div>txttxt<div></div></div>"); expect(fixture.innerHTML).toBe("<div>txttxt<div></div></div>");
}); });
});
describe("miscellaneous", () => {
test("updating widget immediately", async () => { test("updating widget immediately", async () => {
// in this situation, we protect against a bug that occurred: because of the // in this situation, we protect against a bug that occurred: because of the
// interplay between widgets and vnodes, a sub widget vnode was patched // interplay between widgets and vnodes, a sub widget vnode was patched