diff --git a/web/static/src/ts/core/Widget.ts b/web/static/src/ts/core/Widget.ts index 298331d6..82c6ad1a 100644 --- a/web/static/src/ts/core/Widget.ts +++ b/web/static/src/ts/core/Widget.ts @@ -25,6 +25,8 @@ interface Meta { isDestroyed: boolean; parent: Widget | null; children: { [key: number]: Widget }; + // children mapping: from templateID to widgetID + cmap: { [key: number]: number }; } const patch = init([sdListeners, sdAttrs]); @@ -43,6 +45,7 @@ export class Widget { env: T; state: Object = {}; + props: any; refs: { [key: string]: Widget | HTMLElement | undefined } = {}; // either HTMLElement or Widget //-------------------------------------------------------------------------- @@ -51,6 +54,7 @@ export class Widget { constructor(parent: Widget | T, props?: any) { wl.push(this); + this.props = props; let id: number; let p: Widget | null = null; if (parent instanceof Widget) { @@ -69,7 +73,8 @@ export class Widget { isMounted: false, isDestroyed: false, parent: p, - children: {} + children: {}, + cmap: {} }; } @@ -139,6 +144,11 @@ export class Widget { } } + updateProps(props?: any): Promise { + this.props = props; + return this.render(); + } + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- diff --git a/web/static/src/ts/core/qweb_vdom.ts b/web/static/src/ts/core/qweb_vdom.ts index 13035567..39622516 100644 --- a/web/static/src/ts/core/qweb_vdom.ts +++ b/web/static/src/ts/core/qweb_vdom.ts @@ -691,11 +691,6 @@ const widgetDirective: Directive = { priority: 100, atNodeEncounter({ ctx, value, node, qweb }): boolean { ctx.rootContext.shouldDefineOwner = true; - let dummyID = ctx.generateID(); - let defID = ctx.generateID(); - ctx.addLine(`let _${dummyID} = {}; // DUMMY`); - ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`); - ctx.addLine(`c${ctx.parentNode}.push(_${dummyID});`); let props = node.getAttribute("t-props"); if (props) { props = props.trim(); @@ -713,21 +708,48 @@ const widgetDirective: Directive = { props = qweb._formatExpression(props); } } + let dummyID = ctx.generateID(); + let defID = ctx.generateID(); let widgetID = ctx.generateID(); + ctx.addLine(`let _${dummyID} = {}; // DUMMY`); + ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`); + ctx.addLine(`c${ctx.parentNode}.push(_${dummyID});`); + ctx.addLine(`let def${defID};`); + + ctx.addLine(`if (${widgetID} in context.__widget__.cmap) {`); + ctx.indent(); + ctx.addLine( + `let curWidget = context.__widget__.children[context.__widget__.cmap[${widgetID}]]` + ); + ctx.addLine( + `def${defID} = curWidget.updateProps(${props}).then(()=>{vnode=curWidget.__widget__.vnode;c${ + ctx.parentNode + }[_${dummyID}_index]=vnode;vnode.data.hook = {remove(){curWidget.destroy()}}});` + ); + ctx.dedent(); + ctx.addLine("} else {"); + ctx.indent(); + ctx.addLine( `let _${widgetID} = new context.widgets['${value}'](owner, ${props});` ); ctx.addLine( - `let def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{c${ + `context.__widget__.cmap[${widgetID}] = _${widgetID}.__widget__.id` + ); + ctx.addLine( + `def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{c${ ctx.parentNode }[_${dummyID}_index]=vnode;vnode.data.hook = {create(_,vn){_${widgetID}._mount(vn)},remove(){_${widgetID}.destroy()}}});` ); - ctx.addLine(`extra.promises.push(def${defID});`); let ref = node.getAttribute("t-ref"); if (ref) { ctx.addLine(`context.refs['${ref}'] = _${widgetID};`); } + ctx.dedent(); + ctx.addLine("}"); + ctx.addLine(`extra.promises.push(def${defID});`); + return true; } }; diff --git a/web/static/src/ts/widgets/Discuss.ts b/web/static/src/ts/widgets/Discuss.ts index 7ffb560d..9796f4cf 100644 --- a/web/static/src/ts/widgets/Discuss.ts +++ b/web/static/src/ts/widgets/Discuss.ts @@ -56,10 +56,5 @@ export class Discuss extends Widget { class ColorWidget extends Widget { name = "colorwidget"; - template = `
Current Color:
`; - state: { color: "red" | "blue" }; - constructor(parent: Widget, props: { color: "red" | "blue" }) { - super(parent); - this.state = { color: props.color }; - } + template = `
Current Color:
`; } diff --git a/web/static/tests/core/widget.test.ts b/web/static/tests/core/widget.test.ts index a2031a88..28b171b0 100644 --- a/web/static/tests/core/widget.test.ts +++ b/web/static/tests/core/widget.test.ts @@ -516,6 +516,25 @@ describe("composition", () => { expect(children(widget)[0].env).toBe(env); }); + + test("rerendering a widget with a sub widget", async () => { + class ParentWidget extends Widget { + template = `
`; + widgets = { Counter }; + } + const widget = new ParentWidget(env); + await widget.mount(fixture); + const button = fixture.getElementsByTagName("button")[0]; + await button.click(); + await nextTick(); + expect(fixture.innerHTML).toBe( + "
1
" + ); + await widget.render(); + expect(fixture.innerHTML).toBe( + "
1
" + ); + }); }); describe("props evaluation (with t-props directive)", () => {