mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
fix tricky issue with sub widgets modifying el
This commit is contained in:
@@ -30,8 +30,11 @@ export class Widget<T extends WEnv> {
|
|||||||
name: string = "widget";
|
name: string = "widget";
|
||||||
template: string = "<div></div>";
|
template: string = "<div></div>";
|
||||||
|
|
||||||
|
get el(): HTMLElement | null {
|
||||||
|
return this._.vnode ? (<any>this)._.vnode.elm : null;
|
||||||
|
}
|
||||||
|
|
||||||
env: T;
|
env: T;
|
||||||
el: HTMLElement | null = null;
|
|
||||||
state: Object = {};
|
state: Object = {};
|
||||||
refs: { [key: string]: Widget<T> | HTMLElement | undefined } = {}; // either HTMLElement or Widget
|
refs: { [key: string]: Widget<T> | HTMLElement | undefined } = {}; // either HTMLElement or Widget
|
||||||
|
|
||||||
@@ -110,12 +113,11 @@ export class Widget<T extends WEnv> {
|
|||||||
|
|
||||||
async render(): Promise<VNode> {
|
async render(): Promise<VNode> {
|
||||||
const vnode = await this._render();
|
const vnode = await this._render();
|
||||||
if (!this.el) {
|
this._.vnode = patch(
|
||||||
this.el = document.createElement(vnode.sel!);
|
this._.vnode || document.createElement(vnode.sel!),
|
||||||
}
|
vnode
|
||||||
patch(this._.vnode || this.el, vnode);
|
);
|
||||||
this._.vnode = vnode;
|
return this._.vnode;
|
||||||
return vnode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _start(): Promise<void> {
|
private async _start(): Promise<void> {
|
||||||
@@ -130,11 +132,20 @@ export class Widget<T extends WEnv> {
|
|||||||
}
|
}
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
let vnode = this.env.qweb.render(this.name, this, { promises });
|
let vnode = this.env.qweb.render(this.name, this, { promises });
|
||||||
|
|
||||||
|
// this part is critical for the patching process to be done correctly. The
|
||||||
|
// tricky part is that a child widget can be rerendered on its own, which
|
||||||
|
// will update its own vnode representation without the knowledge of the
|
||||||
|
// parent widget. With this, we make sure that the parent widget will be
|
||||||
|
// able to patch itself properly after
|
||||||
|
vnode.key = this._.id;
|
||||||
return Promise.all(promises).then(() => vnode);
|
return Promise.all(promises).then(() => vnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only called by qweb t-widget directive
|
||||||
|
*/
|
||||||
_mount(vnode: VNode) {
|
_mount(vnode: VNode) {
|
||||||
this.el = <HTMLElement>vnode.elm;
|
|
||||||
this._.vnode = vnode;
|
this._.vnode = vnode;
|
||||||
if (this._.parent) {
|
if (this._.parent) {
|
||||||
if (this._.parent._.isMounted) {
|
if (this._.parent._.isMounted) {
|
||||||
|
|||||||
@@ -678,6 +678,7 @@ const widgetDirective: Directive = {
|
|||||||
let dummyID = ctx.generateID();
|
let dummyID = ctx.generateID();
|
||||||
let defID = ctx.generateID();
|
let defID = ctx.generateID();
|
||||||
ctx.addLine(`let _${dummyID} = {} // DUMMY`);
|
ctx.addLine(`let _${dummyID} = {} // DUMMY`);
|
||||||
|
ctx.addLine(`let _${dummyID}_index = c${ctx.parentNode}.length`);
|
||||||
ctx.addLine(`c${ctx.parentNode}.push(_${dummyID})`);
|
ctx.addLine(`c${ctx.parentNode}.push(_${dummyID})`);
|
||||||
let props = node.getAttribute("t-props");
|
let props = node.getAttribute("t-props");
|
||||||
let widgetID = ctx.generateID();
|
let widgetID = ctx.generateID();
|
||||||
@@ -685,7 +686,9 @@ const widgetDirective: Directive = {
|
|||||||
`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=>{Object.assign(_${dummyID}, vnode);_${dummyID}.key="${dummyID}";_${dummyID}.data.hook = {create(_,vn){_${widgetID}._mount(vn)}}})`
|
`let def${defID} = _${widgetID}._start().then(() => _${widgetID}._render()).then(vnode=>{c${
|
||||||
|
ctx.parentNode
|
||||||
|
}[_${dummyID}_index]=vnode;vnode.data.hook = {create(_,vn){_${widgetID}._mount(vn)}}})`
|
||||||
);
|
);
|
||||||
ctx.addLine(`extra.promises.push(def${defID})`);
|
ctx.addLine(`extra.promises.push(def${defID})`);
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ import { Counter } from "./counter";
|
|||||||
const template = `
|
const template = `
|
||||||
<div class="o_discuss">
|
<div class="o_discuss">
|
||||||
<span>DISCUSS!!</span>
|
<span>DISCUSS!!</span>
|
||||||
<button t-on-click="resetCounter">Reset</button>
|
<button t-on-click="resetCounter">Reset first counter</button>
|
||||||
<button t-on-click="resetCounterAsync">Reset in 3s</button>
|
<button t-on-click="resetCounterAsync">Reset counter 2 in 3s</button>
|
||||||
<button t-on-click="toggle">Toggle Counter</button>
|
<button t-on-click="toggle">Toggle Clock/counters</button>
|
||||||
<input/>
|
<input/>
|
||||||
<t t-if="state.validcounter">
|
<t t-if="state.validcounter">
|
||||||
<t t-widget="Counter" t-ref="counter" t-props="{initialState:4}"/>
|
<t t-widget="Counter" t-ref="counter" t-props="{initialState:4}"/>
|
||||||
|
<t t-widget="Counter" t-ref="counter2" t-props="{initialState:400}"/>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="1">
|
<t t-else="1">
|
||||||
<t t-widget="Clock"/>
|
<t t-widget="Clock"/>
|
||||||
@@ -35,15 +36,13 @@ export class Discuss extends Widget<Env> {
|
|||||||
|
|
||||||
resetCounterAsync(ev: MouseEvent) {
|
resetCounterAsync(ev: MouseEvent) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.refs.counter instanceof Counter) {
|
if (this.refs.counter2 instanceof Counter) {
|
||||||
this.refs.counter.updateState({ counter: 3 });
|
this.refs.counter2.updateState({ counter: 300 });
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
if (this.refs.counter instanceof Counter) {
|
this.updateState({ validcounter: !this.state.validcounter });
|
||||||
this.updateState({ validcounter: !this.state.validcounter });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,4 +296,17 @@ describe("composition", () => {
|
|||||||
"<div><div>1<button>Inc</button></div></div>"
|
"<div><div>1<button>Inc</button></div></div>"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("parent's elm for a children === children's elm, even after rerender", async () => {
|
||||||
|
const widget = new WidgetA(env);
|
||||||
|
await widget.mount(fixture);
|
||||||
|
|
||||||
|
expect((<any>widget._.vnode!.children![1]).elm).toBe(
|
||||||
|
(<any>widget._.children[0]._.vnode).elm
|
||||||
|
);
|
||||||
|
await widget._.children[0].render();
|
||||||
|
expect((<any>widget._.vnode!.children![1]).elm).toBe(
|
||||||
|
(<any>widget._.children[0]._.vnode).elm
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user