mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
work on mounted hook, make sure widgets are uniquely defined...
This commit is contained in:
@@ -16,6 +16,7 @@ export class Widget<T extends WEnv> {
|
|||||||
vnode: VNode | null = null;
|
vnode: VNode | null = null;
|
||||||
|
|
||||||
isStarted: boolean = false;
|
isStarted: boolean = false;
|
||||||
|
isMounted: boolean = false;
|
||||||
parent: Widget<T> | null = null;
|
parent: Widget<T> | null = null;
|
||||||
children: Widget<T>[] = [];
|
children: Widget<T>[] = [];
|
||||||
env: T;
|
env: T;
|
||||||
@@ -48,17 +49,20 @@ export class Widget<T extends WEnv> {
|
|||||||
// Public
|
// Public
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
async mount(target?: HTMLElement): Promise<VNode> {
|
async mount(target: HTMLElement): Promise<VNode> {
|
||||||
await this.willStart();
|
await this._start();
|
||||||
this.isStarted = true;
|
|
||||||
const vnode = await this.render();
|
const vnode = await this.render();
|
||||||
|
target.appendChild(this.el!);
|
||||||
|
|
||||||
if (target) {
|
if (document.body.contains(target)) {
|
||||||
target.appendChild(this.el!);
|
this.visitSubTree(w => {
|
||||||
if (document.body.contains(target)) {
|
if (!w.isMounted) {
|
||||||
this.visitSubTree(w => w.mounted());
|
w.isMounted = true;
|
||||||
}
|
w.mounted();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
return vnode;
|
return vnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +97,11 @@ export class Widget<T extends WEnv> {
|
|||||||
return vnode;
|
return vnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _start(): Promise<void> {
|
||||||
|
await this.willStart();
|
||||||
|
this.isStarted = true;
|
||||||
|
}
|
||||||
|
|
||||||
private async _render(): Promise<VNode> {
|
private async _render(): Promise<VNode> {
|
||||||
if (this.template) {
|
if (this.template) {
|
||||||
this.env.qweb.addTemplate(this.name, this.template);
|
this.env.qweb.addTemplate(this.name, this.template);
|
||||||
@@ -103,6 +112,16 @@ export class Widget<T extends WEnv> {
|
|||||||
return Promise.all(promises).then(() => vnode);
|
return Promise.all(promises).then(() => vnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_mount(el: HTMLElement) {
|
||||||
|
this.el = el;
|
||||||
|
if (this.parent) {
|
||||||
|
if (this.parent.isMounted) {
|
||||||
|
this.isMounted = true;
|
||||||
|
this.mounted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private visitSubTree(callback: (w: Widget<T>) => void) {
|
private visitSubTree(callback: (w: Widget<T>) => void) {
|
||||||
callback(this);
|
callback(this);
|
||||||
for (let child of this.children) {
|
for (let child of this.children) {
|
||||||
|
|||||||
@@ -684,7 +684,7 @@ const widgetDirective: Directive = {
|
|||||||
`let _${widgetID} = new context.widgets['${value}'](context, ${props})`
|
`let _${widgetID} = new context.widgets['${value}'](context, ${props})`
|
||||||
);
|
);
|
||||||
ctx.addLine(
|
ctx.addLine(
|
||||||
`let def${defID} = _${widgetID}._render().then(vnode=>{Object.assign(_${dummyID}, vnode);_${dummyID}.data.hook = {create(_,vn){_${widgetID}.el=vn.elm}}})`
|
`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.elm)}}})`
|
||||||
);
|
);
|
||||||
ctx.addLine(`extra.promises.push(def${defID})`);
|
ctx.addLine(`extra.promises.push(def${defID})`);
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ export class Counter extends Widget<Env> {
|
|||||||
this.state.counter = props.initialState || 0;
|
this.state.counter = props.initialState || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
|
||||||
console.log("counter mounter", this.el);
|
|
||||||
}
|
|
||||||
increment(delta: number) {
|
increment(delta: number) {
|
||||||
this.updateState({ counter: this.state.counter + delta });
|
this.updateState({ counter: this.state.counter + delta });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ export class Navbar extends Widget<Env> {
|
|||||||
name = "navbar";
|
name = "navbar";
|
||||||
template = template;
|
template = template;
|
||||||
|
|
||||||
mounted() {
|
|
||||||
console.log("navbar mounted", this.el);
|
|
||||||
}
|
|
||||||
getUrl(menu: Menu) {
|
getUrl(menu: Menu) {
|
||||||
const action_id = String(menu.actionID);
|
const action_id = String(menu.actionID);
|
||||||
return this.env.router.formatURL("", { action_id });
|
return this.env.router.formatURL("", { action_id });
|
||||||
|
|||||||
@@ -15,12 +15,10 @@ export class Clock extends Widget<Env> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
console.log("clock mounter", this.el);
|
|
||||||
setInterval(this.updateTime.bind(this), 500);
|
setInterval(this.updateTime.bind(this), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTime() {
|
updateTime() {
|
||||||
debugger;
|
|
||||||
this.updateState({
|
this.updateState({
|
||||||
currentTime: new Date().toLocaleTimeString()
|
currentTime: new Date().toLocaleTimeString()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -130,6 +130,27 @@ describe("lifecycle hooks", () => {
|
|||||||
target.remove();
|
target.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("willStart hook is called on subwidget", async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
let ok = false;
|
||||||
|
class ParentWidget extends Widget<TestEnv> {
|
||||||
|
name = "a";
|
||||||
|
template = `<div><t t-widget="child"/></div>`;
|
||||||
|
widgets = { child: ChildWidget };
|
||||||
|
}
|
||||||
|
class ChildWidget extends Widget<TestEnv> {
|
||||||
|
async willStart() {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const widget = makeWidget(ParentWidget);
|
||||||
|
const target = document.createElement("div");
|
||||||
|
document.body.appendChild(target);
|
||||||
|
await widget.mount(target);
|
||||||
|
expect(ok).toBe(true);
|
||||||
|
target.remove();
|
||||||
|
});
|
||||||
|
|
||||||
test("mounted hook is called on subwidgets, in proper order", async () => {
|
test("mounted hook is called on subwidgets, in proper order", async () => {
|
||||||
expect.assertions(4);
|
expect.assertions(4);
|
||||||
let parentMounted = false;
|
let parentMounted = false;
|
||||||
@@ -157,6 +178,44 @@ describe("lifecycle hooks", () => {
|
|||||||
expect(childMounted).toBe(true);
|
expect(childMounted).toBe(true);
|
||||||
target.remove();
|
target.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("willStart, mounted on subwidget rendered after main is mounted in some other position", async () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
let hookCounter = 0;
|
||||||
|
class ParentWidget extends Widget<TestEnv> {
|
||||||
|
name = "a";
|
||||||
|
state = { ok: false };
|
||||||
|
template = `
|
||||||
|
<div>
|
||||||
|
<t t-if="state.ok">
|
||||||
|
<t t-widget="child"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="1">
|
||||||
|
<div/>
|
||||||
|
</t>
|
||||||
|
</div>`; // the t-else part in this template is important. This is
|
||||||
|
// necessary to have a situation that could confuse the vdom
|
||||||
|
// patching algorithm
|
||||||
|
widgets = { child: ChildWidget };
|
||||||
|
}
|
||||||
|
class ChildWidget extends Widget<TestEnv> {
|
||||||
|
async willStart() {
|
||||||
|
hookCounter++;
|
||||||
|
}
|
||||||
|
mounted() {
|
||||||
|
expect(hookCounter).toBe(1);
|
||||||
|
hookCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const widget = makeWidget(ParentWidget);
|
||||||
|
const target = document.createElement("div");
|
||||||
|
document.body.appendChild(target);
|
||||||
|
await widget.mount(target);
|
||||||
|
expect(hookCounter).toBe(0); // sub widget not created yet
|
||||||
|
await widget.updateState({ ok: true });
|
||||||
|
expect(hookCounter).toBe(2);
|
||||||
|
target.remove();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("destroy method", () => {
|
describe("destroy method", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user