mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
fix issue with vnodes being patched twice
This commit is contained in:
@@ -55,7 +55,6 @@ export class Widget<T extends WEnv, Props> {
|
||||
|
||||
constructor(parent: Widget<T, {}> | T, props?: Props) {
|
||||
wl.push(this);
|
||||
|
||||
// is this a good idea?
|
||||
// Pro: if props is empty, we can create easily a widget
|
||||
// Con: this is not really safe
|
||||
@@ -211,14 +210,15 @@ export class Widget<T extends WEnv, Props> {
|
||||
/**
|
||||
* Only called by qweb t-widget directive
|
||||
*/
|
||||
_mount(vnode: VNode) {
|
||||
this.__widget__.vnode = vnode;
|
||||
_mount(vnode: VNode, elm: HTMLElement): VNode {
|
||||
this.__widget__.vnode = patch(elm, vnode);
|
||||
if (this.__widget__.parent) {
|
||||
if (this.__widget__.parent.__widget__.isMounted) {
|
||||
this.__widget__.isMounted = true;
|
||||
this.mounted();
|
||||
}
|
||||
}
|
||||
return this.__widget__.vnode;
|
||||
}
|
||||
|
||||
private visitSubTree(callback: (w: Widget<T, any>) => void) {
|
||||
|
||||
@@ -210,14 +210,14 @@ export class QWeb {
|
||||
const mainNode = doc.firstChild!;
|
||||
this._compileNode(mainNode, ctx);
|
||||
|
||||
if (ctx.shouldProtectContext) {
|
||||
ctx.code.unshift(" context = Object.create(context);");
|
||||
}
|
||||
if (ctx.shouldDefineOwner) {
|
||||
// this is necessary to prevent some directives (t-forach for ex) to
|
||||
// pollute the rendering context by adding some keys in it.
|
||||
ctx.code.unshift(" let owner = context;");
|
||||
}
|
||||
if (ctx.shouldProtectContext) {
|
||||
ctx.code.unshift(" context = Object.create(context);");
|
||||
}
|
||||
|
||||
if (!ctx.rootNode) {
|
||||
throw new Error("A template should have one root node");
|
||||
@@ -728,7 +728,6 @@ const widgetDirective: Directive = {
|
||||
let dummyID = ctx.generateID();
|
||||
let defID = ctx.generateID();
|
||||
let widgetID = ctx.generateID();
|
||||
ctx.addLine(`let _${dummyID} = {}; // DUMMY`);
|
||||
let keyID = key && ctx.generateID();
|
||||
if (key) {
|
||||
// we bind a variable to the key (could be a complex expression, so we
|
||||
@@ -736,7 +735,7 @@ const widgetDirective: Directive = {
|
||||
ctx.addLine(`let key${keyID} = ${key};`);
|
||||
}
|
||||
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length;`);
|
||||
ctx.addLine(`c${ctx.parentNode}.push(_${dummyID});`);
|
||||
ctx.addLine(`c${ctx.parentNode}.push(null);`);
|
||||
ctx.addLine(`let def${defID};`);
|
||||
let templateID = key
|
||||
? `key${keyID}`
|
||||
@@ -746,11 +745,10 @@ const widgetDirective: Directive = {
|
||||
ctx.addLine(
|
||||
`let w${widgetID} = ${templateID} in context.__widget__.cmap ? context.__widget__.children[context.__widget__.cmap[${templateID}]] : false;`
|
||||
);
|
||||
|
||||
ctx.addLine(`if (w${widgetID}) {`);
|
||||
ctx.indent();
|
||||
ctx.addLine(
|
||||
`def${defID} = w${widgetID}.updateProps(${props}).then(()=>{vnode=w${widgetID}.__widget__.vnode;c${
|
||||
`def${defID} = w${widgetID}.updateProps(${props}).then(()=>{let vnode=h(w${widgetID}.__widget__.vnode.sel, {key: ${templateID}});c${
|
||||
ctx.parentNode
|
||||
}[_${dummyID}_index]=vnode;vnode.data.hook = {remove(){w${widgetID}.destroy()}}});`
|
||||
);
|
||||
@@ -765,9 +763,9 @@ const widgetDirective: Directive = {
|
||||
`context.__widget__.cmap[${templateID}] = _${widgetID}.__widget__.id;`
|
||||
);
|
||||
ctx.addLine(
|
||||
`def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{c${
|
||||
`def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{let pvnode=h(vnode.sel, {key: ${templateID}});c${
|
||||
ctx.parentNode
|
||||
}[_${dummyID}_index]=vnode;vnode.data.hook = {create(_,vn){_${widgetID}._mount(vn)},remove(){_${widgetID}.destroy()}}});`
|
||||
}[_${dummyID}_index]=pvnode;pvnode.data.hook = {insert(vn){let nvn=_${widgetID}._mount(vnode, vn.elm);pvnode.elm=nvn.elm},remove(){_${widgetID}.destroy()}}});`
|
||||
);
|
||||
|
||||
let ref = node.getAttribute("t-ref");
|
||||
|
||||
@@ -22,16 +22,28 @@ const template = `
|
||||
<t t-widget="ColorWidget" t-props="{color: state.color}"/>
|
||||
<button t-on-click="addNotif(false)">Add notif</button>
|
||||
<button t-on-click="addNotif(true)">Add sticky notif</button>
|
||||
<t t-foreach="state.test" t-as="blip">
|
||||
<t t-widget="Counter" t-key="blip" t-props="{initialState: 100}"/>
|
||||
<t t-foreach="state.test" t-as="number">
|
||||
<t t-widget="ChildWidget" t-key="number"/>
|
||||
</t>
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
let n = 1;
|
||||
class ChildWidget extends Widget<Env, never> {
|
||||
name = "c";
|
||||
template = `<span><t t-esc="state.n"/></span>`;
|
||||
constructor(parent) {
|
||||
super(parent);
|
||||
this.state = { n };
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
export class Discuss extends Widget<Env, {}> {
|
||||
name = "discuss";
|
||||
template = template;
|
||||
widgets = { Clock, Counter, ColorWidget };
|
||||
widgets = { Clock, Counter, ColorWidget, ChildWidget };
|
||||
state = { validcounter: true, color: "red", test: [1, 2, 3] };
|
||||
|
||||
mounted() {}
|
||||
|
||||
@@ -714,3 +714,27 @@ describe("random stuff", () => {
|
||||
expect(fixture.innerHTML).toBe("<div>txttxt<div></div></div>");
|
||||
});
|
||||
});
|
||||
|
||||
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
|
||||
// twice.
|
||||
class Parent extends Widget<WEnv, {}> {
|
||||
name = "a";
|
||||
template = `<div><t t-widget="child" t-props="state"/></div>`;
|
||||
widgets = { child: Child };
|
||||
state = { flag: false };
|
||||
}
|
||||
|
||||
class Child extends Widget<WEnv, {}> {
|
||||
template = `<span>abc<t t-if="props.flag">def</t></span>`;
|
||||
}
|
||||
|
||||
const widget = new Parent(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>abc</span></div>");
|
||||
await widget.updateState({ flag: true });
|
||||
expect(fixture.innerHTML).toBe("<div><span>abcdef</span></div>");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user