diff --git a/demo/src/RootWidget.ts b/demo/src/RootWidget.ts index 36cac83c..36056a15 100644 --- a/demo/src/RootWidget.ts +++ b/demo/src/RootWidget.ts @@ -5,6 +5,7 @@ const template = `
Root Widget + @@ -23,6 +24,12 @@ export default class RootWidget extends Widget { this.refs.counter.updateState({counter: 3}) } + resetCounterAsync(ev: MouseEvent) { + setTimeout(() => { + this.refs.counter.updateState({counter: 3}) + }, 3000); + } + toggle() { this.updateState({validcounter: !this.state.validcounter}); } diff --git a/demo/src/main.ts b/demo/src/main.ts index f9f926b8..11543bbe 100644 --- a/demo/src/main.ts +++ b/demo/src/main.ts @@ -11,12 +11,12 @@ // (window).h = h; // (window).patch = patch; // (window).QWeb = QWeb; -import Counter from "./Counter"; -// import RootWidget from "./RootWidget"; +// import Counter from "./Counter"; +import RootWidget from "./RootWidget"; import env from "./env"; document.addEventListener("DOMContentLoaded", async function() { - const rootWidget = new Counter(null, {}); + const rootWidget = new RootWidget(null); rootWidget.setEnvironment(env); const mainDiv = document.getElementById("app")!; await rootWidget.mount(mainDiv); diff --git a/src/core/qweb_vdom.ts b/src/core/qweb_vdom.ts index e33fdbba..fa6ebf30 100644 --- a/src/core/qweb_vdom.ts +++ b/src/core/qweb_vdom.ts @@ -96,7 +96,8 @@ export default class QWeb { elifDirective, ifDirective, callDirective, - onDirective + onDirective, + widgetDirective ].forEach(d => this.addDirective(d)); } @@ -641,21 +642,25 @@ const onDirective: Directive = { } }; -// const widgetDirective: Directive = { -// name: "widget", -// priority: 100, -// atNodeEncounter({ ctx, fullName, value, node, qweb}): boolean { -// let spanID = ctx.generateID(); -// ctx.addLine(`let ${spanID} = document.createElement('span')`); -// ctx.addNode(spanID) -// let props = node.getAttribute('t-props'); -// let widgetID = ctx.generateID(); -// ctx.addLine(`let ${widgetID} = new context.widgets['${value}'](context, ${props})`); -// ctx.addLine(`${widgetID}.mount(${spanID}).then(()=>${ctx.parentNode}.replaceChild(${widgetID}.el, ${spanID}))`); -// let ref = node.getAttribute('t-ref'); -// if (ref) { -// ctx.addLine(`context.refs['${ref}'] = ${widgetID}`); -// } -// return true; -// } -// }; +const widgetDirective: Directive = { + name: "widget", + priority: 100, + atNodeEncounter({ ctx, fullName, value, node, qweb}): boolean { + let dummyID = ctx.generateID(); + let defID = ctx.generateID(); + ctx.addLine(`let _${dummyID} = {} // DUMMY`); + ctx.addLine(`c${ctx.parentNode}.push(_${dummyID})`); + let props = node.getAttribute('t-props'); + let widgetID = ctx.generateID(); + ctx.addLine(`let _${widgetID} = new context.widgets['${value}'](context, ${props})`); + ctx.addLine(`let def${defID} = _${widgetID}.mount().then(vnode=>Object.assign(_${dummyID}, vnode))`); + ctx.addLine(`context._TEMP.push(def${defID})`); + + // split into extra directive? + let ref = node.getAttribute('t-ref'); + if (ref) { + ctx.addLine(`context.refs['${ref}'] = _${widgetID}`); + } + return true; + } +}; diff --git a/src/core/widget.ts b/src/core/widget.ts index 2d86953e..692ec3e6 100644 --- a/src/core/widget.ts +++ b/src/core/widget.ts @@ -17,6 +17,7 @@ export default class Widget { name: string = "widget"; template: string = "
"; vnode: VNode | null = null; + _TEMP: Promise[] | null = null; parent: Widget | null; children: Widget[] = []; @@ -50,13 +51,16 @@ export default class Widget { // Public //-------------------------------------------------------------------------- - async mount(target: HTMLElement) { + async mount(target?: HTMLElement): Promise { await this.willStart(); this.env!.qweb.addTemplate(this.name, this.template); delete this.template; - await this.render(); + const vnode = await this.render(); - target.appendChild(this.el!); + if (target) { + target.appendChild(this.el!); + } + return vnode; } destroy() { @@ -84,19 +88,15 @@ export default class Widget { // Private //-------------------------------------------------------------------------- - async render() { - let vnode = await this.env!.qweb.render(this.name, this); + async render(): Promise { + this._TEMP = []; + let vnode = this.env!.qweb.render(this.name, this); + await Promise.all(this._TEMP); if (!this.el) { this.el = document.createElement(vnode.sel!); } patch(this.vnode || this.el, vnode); this.vnode = vnode; + return vnode; } - - // private _setElement(el: ChildNode) { - // if (this.el) { - // this.el.replaceWith(el); - // } - // this.el = el; - // } } diff --git a/tests/qweb_vdom.test.ts b/tests/qweb_vdom.test.ts index 402d79e3..bba80013 100644 --- a/tests/qweb_vdom.test.ts +++ b/tests/qweb_vdom.test.ts @@ -311,7 +311,7 @@ describe("attributes", () => { ); }); - xit("various escapes", () => { + test.skip("various escapes", () => { // need to think about this... This one does not pass, but I am not sure it is // a correct test const template = ` diff --git a/tests/widget.test.ts b/tests/widget.test.ts index f96cee7f..ae4a59ac 100644 --- a/tests/widget.test.ts +++ b/tests/widget.test.ts @@ -93,3 +93,23 @@ describe("destroy method", () => { expect(document.contains(widget.el)).toBe(false); }); }); + +describe("composition", () => { + test("a widget with a sub widget", async () => { + + class WidgetB extends Widget { + template= `
world
`; + } + + class WidgetA extends Widget { + name="a"; + template= `
Hello
`; + widgets = {b: WidgetB} + } + + const widget = makeWidget(WidgetA); + const target = document.createElement("div"); + await widget.mount(target); + expect(target.innerHTML).toBe("
Hello
world
"); + }); +});