mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
do not destroy and rerender sub widgets if not necessary
add failing test on rerendering sub widgets...
This commit is contained in:
@@ -25,6 +25,8 @@ interface Meta<T extends WEnv> {
|
|||||||
isDestroyed: boolean;
|
isDestroyed: boolean;
|
||||||
parent: Widget<T> | null;
|
parent: Widget<T> | null;
|
||||||
children: { [key: number]: Widget<T> };
|
children: { [key: number]: Widget<T> };
|
||||||
|
// children mapping: from templateID to widgetID
|
||||||
|
cmap: { [key: number]: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = init([sdListeners, sdAttrs]);
|
const patch = init([sdListeners, sdAttrs]);
|
||||||
@@ -43,6 +45,7 @@ export class Widget<T extends WEnv> {
|
|||||||
|
|
||||||
env: T;
|
env: T;
|
||||||
state: Object = {};
|
state: Object = {};
|
||||||
|
props: any;
|
||||||
refs: { [key: string]: Widget<T> | HTMLElement | undefined } = {}; // either HTMLElement or Widget
|
refs: { [key: string]: Widget<T> | HTMLElement | undefined } = {}; // either HTMLElement or Widget
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -51,6 +54,7 @@ export class Widget<T extends WEnv> {
|
|||||||
|
|
||||||
constructor(parent: Widget<T> | T, props?: any) {
|
constructor(parent: Widget<T> | T, props?: any) {
|
||||||
wl.push(this);
|
wl.push(this);
|
||||||
|
this.props = props;
|
||||||
let id: number;
|
let id: number;
|
||||||
let p: Widget<T> | null = null;
|
let p: Widget<T> | null = null;
|
||||||
if (parent instanceof Widget) {
|
if (parent instanceof Widget) {
|
||||||
@@ -69,7 +73,8 @@ export class Widget<T extends WEnv> {
|
|||||||
isMounted: false,
|
isMounted: false,
|
||||||
isDestroyed: false,
|
isDestroyed: false,
|
||||||
parent: p,
|
parent: p,
|
||||||
children: {}
|
children: {},
|
||||||
|
cmap: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +144,11 @@ export class Widget<T extends WEnv> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateProps(props?: any): Promise<void> {
|
||||||
|
this.props = props;
|
||||||
|
return this.render();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -691,11 +691,6 @@ const widgetDirective: Directive = {
|
|||||||
priority: 100,
|
priority: 100,
|
||||||
atNodeEncounter({ ctx, value, node, qweb }): boolean {
|
atNodeEncounter({ ctx, value, node, qweb }): boolean {
|
||||||
ctx.rootContext.shouldDefineOwner = true;
|
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");
|
let props = node.getAttribute("t-props");
|
||||||
if (props) {
|
if (props) {
|
||||||
props = props.trim();
|
props = props.trim();
|
||||||
@@ -713,21 +708,48 @@ const widgetDirective: Directive = {
|
|||||||
props = qweb._formatExpression(props);
|
props = qweb._formatExpression(props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let dummyID = ctx.generateID();
|
||||||
|
let defID = ctx.generateID();
|
||||||
let widgetID = 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(
|
ctx.addLine(
|
||||||
`let _${widgetID} = new context.widgets['${value}'](owner, ${props});`
|
`let _${widgetID} = new context.widgets['${value}'](owner, ${props});`
|
||||||
);
|
);
|
||||||
ctx.addLine(
|
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
|
ctx.parentNode
|
||||||
}[_${dummyID}_index]=vnode;vnode.data.hook = {create(_,vn){_${widgetID}._mount(vn)},remove(){_${widgetID}.destroy()}}});`
|
}[_${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");
|
let ref = node.getAttribute("t-ref");
|
||||||
if (ref) {
|
if (ref) {
|
||||||
ctx.addLine(`context.refs['${ref}'] = _${widgetID};`);
|
ctx.addLine(`context.refs['${ref}'] = _${widgetID};`);
|
||||||
}
|
}
|
||||||
|
ctx.dedent();
|
||||||
|
ctx.addLine("}");
|
||||||
|
ctx.addLine(`extra.promises.push(def${defID});`);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,10 +56,5 @@ export class Discuss extends Widget<Env> {
|
|||||||
|
|
||||||
class ColorWidget extends Widget<Env> {
|
class ColorWidget extends Widget<Env> {
|
||||||
name = "colorwidget";
|
name = "colorwidget";
|
||||||
template = `<div>Current Color: <t t-esc="state.color"/></div>`;
|
template = `<div>Current Color: <t t-esc="props.color"/></div>`;
|
||||||
state: { color: "red" | "blue" };
|
|
||||||
constructor(parent: Widget<Env>, props: { color: "red" | "blue" }) {
|
|
||||||
super(parent);
|
|
||||||
this.state = { color: props.color };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -516,6 +516,25 @@ describe("composition", () => {
|
|||||||
|
|
||||||
expect(children(widget)[0].env).toBe(env);
|
expect(children(widget)[0].env).toBe(env);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("rerendering a widget with a sub widget", async () => {
|
||||||
|
class ParentWidget extends Widget<WEnv> {
|
||||||
|
template = `<div><t t-widget="Counter"/></div>`;
|
||||||
|
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(
|
||||||
|
"<div><div>1<button>Inc</button></div></div>"
|
||||||
|
);
|
||||||
|
await widget.render();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><div>1<button>Inc</button></div></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("props evaluation (with t-props directive)", () => {
|
describe("props evaluation (with t-props directive)", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user